@orkestrel/agent 0.0.41 → 0.0.43
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/src/core/index.cjs +2820 -523
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +1685 -266
- package/dist/src/core/index.d.ts +1685 -266
- package/dist/src/core/index.js +2742 -525
- package/dist/src/core/index.js.map +1 -1
- package/package.json +7 -7
package/dist/src/core/index.cjs
CHANGED
|
@@ -3,13 +3,13 @@ let _orkestrel_contract = require("@orkestrel/contract");
|
|
|
3
3
|
let _orkestrel_budget = require("@orkestrel/budget");
|
|
4
4
|
let _orkestrel_tool = require("@orkestrel/tool");
|
|
5
5
|
let _orkestrel_ndjson = require("@orkestrel/ndjson");
|
|
6
|
+
let _orkestrel_abort = require("@orkestrel/abort");
|
|
6
7
|
let _orkestrel_timeout = require("@orkestrel/timeout");
|
|
7
8
|
let _orkestrel_database = require("@orkestrel/database");
|
|
8
9
|
let _orkestrel_emitter = require("@orkestrel/emitter");
|
|
9
10
|
let _orkestrel_workspace = require("@orkestrel/workspace");
|
|
10
11
|
let _orkestrel_queue = require("@orkestrel/queue");
|
|
11
12
|
let _orkestrel_workflow = require("@orkestrel/workflow");
|
|
12
|
-
let _orkestrel_abort = require("@orkestrel/abort");
|
|
13
13
|
// src/core/constants.ts
|
|
14
14
|
/**
|
|
15
15
|
* Lists the roles any message can play, the built `system` head included, in the order the wire
|
|
@@ -726,6 +726,23 @@ function collectToolGroups(messages) {
|
|
|
726
726
|
return [...groups, ...orphans];
|
|
727
727
|
}
|
|
728
728
|
/**
|
|
729
|
+
* Finds the call that owns a tool message within a collected tool group.
|
|
730
|
+
* @param group - The assistant leader followed by its tool results, as {@link collectToolGroups} returns it
|
|
731
|
+
* @param message - The tool result to pair; distinct leader ids pair by result id when any result has one
|
|
732
|
+
* @returns The call matching the result's call id, or its position when the leader repeats an id or every result lacks an id; undefined when unpaired
|
|
733
|
+
* @example
|
|
734
|
+
* ```ts
|
|
735
|
+
* findToolCall(group, result)?.arguments
|
|
736
|
+
* ```
|
|
737
|
+
*/
|
|
738
|
+
function findToolCall(group, message) {
|
|
739
|
+
const [leader, ...results] = group;
|
|
740
|
+
const at = results.findIndex((result) => result.id === message.id);
|
|
741
|
+
if (message.role !== "tool" || at < 0) return void 0;
|
|
742
|
+
const calls = leader?.calls ?? [];
|
|
743
|
+
return !(new Set(calls.map((call) => call.id)).size !== calls.length) && results.some((result) => result.call !== void 0) ? calls.find((call) => call.id === message.call) : calls[at];
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
729
746
|
* Renders a value as the JSON text `JSON.stringify` produces, or `undefined` when JSON cannot
|
|
730
747
|
* carry the value.
|
|
731
748
|
*
|
|
@@ -1023,6 +1040,60 @@ var REFUSED_RELAY_STATUS = 422;
|
|
|
1023
1040
|
var REFUSED_RELAY_CONTENT_TYPE = "text/plain; charset=utf-8";
|
|
1024
1041
|
/** Names the System One decision endpoint shared by compatible servers. */
|
|
1025
1042
|
var SYSTEM_ONE_PATH = "/v1/systemone";
|
|
1043
|
+
/** Lists the members `RelayOptions` declares, frozen — the keys `createRelay` admits. */
|
|
1044
|
+
var RELAY_OPTION_KEYS = Object.freeze([
|
|
1045
|
+
"provider",
|
|
1046
|
+
"authorize",
|
|
1047
|
+
"limit",
|
|
1048
|
+
"system",
|
|
1049
|
+
"tools"
|
|
1050
|
+
]);
|
|
1051
|
+
/** Lists the members `RelayProviderOptions` declares, frozen — the keys the `RelayProvider` constructor admits. */
|
|
1052
|
+
var RELAY_PROVIDER_KEYS = Object.freeze([
|
|
1053
|
+
"url",
|
|
1054
|
+
"parser",
|
|
1055
|
+
"timeout",
|
|
1056
|
+
"replay",
|
|
1057
|
+
"fetch",
|
|
1058
|
+
"headers"
|
|
1059
|
+
]);
|
|
1060
|
+
/** Lists the members `RelayStreamOptions` declares, frozen — the keys the `RelayStream` constructor admits. */
|
|
1061
|
+
var RELAY_STREAM_KEYS = Object.freeze([
|
|
1062
|
+
"provider",
|
|
1063
|
+
"request",
|
|
1064
|
+
"signal"
|
|
1065
|
+
]);
|
|
1066
|
+
/** Lists the members `AgentProviderInput` declares, frozen — the keys the `AgentProvider` constructor admits. */
|
|
1067
|
+
var PROVIDER_INPUT_KEYS = Object.freeze([
|
|
1068
|
+
"url",
|
|
1069
|
+
"path",
|
|
1070
|
+
"split",
|
|
1071
|
+
"strict",
|
|
1072
|
+
"timeout",
|
|
1073
|
+
"replay",
|
|
1074
|
+
"fetch",
|
|
1075
|
+
"headers"
|
|
1076
|
+
]);
|
|
1077
|
+
/** Lists the members `AgentJudgeInput` declares, frozen — the keys the `AgentJudge` constructor admits. */
|
|
1078
|
+
var JUDGE_INPUT_KEYS = Object.freeze([
|
|
1079
|
+
"url",
|
|
1080
|
+
"path",
|
|
1081
|
+
"model",
|
|
1082
|
+
"batch",
|
|
1083
|
+
"timeout",
|
|
1084
|
+
"fetch",
|
|
1085
|
+
"headers"
|
|
1086
|
+
]);
|
|
1087
|
+
/** Lists the members `SystemOneJudgeOptions` declares, frozen — the keys the `SystemOneJudge` constructor admits. */
|
|
1088
|
+
var SYSTEM_ONE_OPTION_KEYS = Object.freeze([
|
|
1089
|
+
"url",
|
|
1090
|
+
"model",
|
|
1091
|
+
"timeout",
|
|
1092
|
+
"fetch",
|
|
1093
|
+
"headers"
|
|
1094
|
+
]);
|
|
1095
|
+
/** Lists the members `ProviderStreamOptions` declares, frozen — the keys the `generate` and `stream` methods of an `AgentProvider` admits. */
|
|
1096
|
+
var STREAM_OPTION_KEYS = Object.freeze(["think", "schema"]);
|
|
1026
1097
|
// src/core/providers/errors.ts
|
|
1027
1098
|
/**
|
|
1028
1099
|
* Reports a provider stream cancelled mid-flight by its bound signal — thrown by the
|
|
@@ -2039,6 +2110,7 @@ var RelayStream = class {
|
|
|
2039
2110
|
#response;
|
|
2040
2111
|
#settled = false;
|
|
2041
2112
|
constructor(options) {
|
|
2113
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, RELAY_STREAM_KEYS, "RelayStream", "RelayStreamOptions");
|
|
2042
2114
|
this.#replay = options.provider.replay ?? "none";
|
|
2043
2115
|
this.#signal = options.signal;
|
|
2044
2116
|
this.#listener = this.#abort.bind(this);
|
|
@@ -2305,6 +2377,7 @@ var AgentProvider = class {
|
|
|
2305
2377
|
#split;
|
|
2306
2378
|
#strict;
|
|
2307
2379
|
constructor(input) {
|
|
2380
|
+
(0, _orkestrel_contract.refuseOptionKeys)(input, PROVIDER_INPUT_KEYS, "AgentProvider", "AgentProviderInput");
|
|
2308
2381
|
this.#id = crypto.randomUUID();
|
|
2309
2382
|
this.#replay = input.replay ?? "none";
|
|
2310
2383
|
this.#url = input.url;
|
|
@@ -2352,10 +2425,11 @@ var AgentProvider = class {
|
|
|
2352
2425
|
* is malformed or carries an upstream failure, or a strict stream ends without a settled result
|
|
2353
2426
|
*/
|
|
2354
2427
|
async *stream(messages, signal, tools, options) {
|
|
2428
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, STREAM_OPTION_KEYS, "AgentProvider", "ProviderStreamOptions");
|
|
2355
2429
|
if (signal.aborted) throw new ProviderAbortError({ content: "" });
|
|
2356
2430
|
const timeout = new _orkestrel_timeout.Timeout({ ms: this.#timeout });
|
|
2357
2431
|
timeout.start();
|
|
2358
|
-
const combined =
|
|
2432
|
+
const combined = (0, _orkestrel_abort.linkSignal)(timeout.signal, signal);
|
|
2359
2433
|
try {
|
|
2360
2434
|
const parser = this.#frame(combined);
|
|
2361
2435
|
try {
|
|
@@ -2507,6 +2581,7 @@ var AgentProvider = class {
|
|
|
2507
2581
|
var RelayProvider = class extends AgentProvider {
|
|
2508
2582
|
#parser;
|
|
2509
2583
|
constructor(options) {
|
|
2584
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, RELAY_PROVIDER_KEYS, "RelayProvider", "RelayProviderOptions");
|
|
2510
2585
|
const { url, timeout, fetch, headers, replay } = options;
|
|
2511
2586
|
super({
|
|
2512
2587
|
url,
|
|
@@ -2639,6 +2714,7 @@ var AgentJudge = class {
|
|
|
2639
2714
|
#headers;
|
|
2640
2715
|
#batch;
|
|
2641
2716
|
constructor(input) {
|
|
2717
|
+
(0, _orkestrel_contract.refuseOptionKeys)(input, JUDGE_INPUT_KEYS, "AgentJudge", "AgentJudgeInput");
|
|
2642
2718
|
this.#id = crypto.randomUUID();
|
|
2643
2719
|
this.#url = input.url;
|
|
2644
2720
|
this.#path = input.path ?? "";
|
|
@@ -2703,7 +2779,7 @@ var AgentJudge = class {
|
|
|
2703
2779
|
async #call(request, body, signal, completed) {
|
|
2704
2780
|
const timeout = new _orkestrel_timeout.Timeout({ ms: this.#timeout });
|
|
2705
2781
|
timeout.start();
|
|
2706
|
-
const combined =
|
|
2782
|
+
const combined = (0, _orkestrel_abort.linkSignal)(timeout.signal, signal);
|
|
2707
2783
|
try {
|
|
2708
2784
|
const headers = await buildProviderHeaders(this.#headers, combined);
|
|
2709
2785
|
combined.throwIfAborted();
|
|
@@ -2804,6 +2880,7 @@ var AgentJudge = class {
|
|
|
2804
2880
|
*/
|
|
2805
2881
|
var SystemOneJudge = class extends AgentJudge {
|
|
2806
2882
|
constructor(options) {
|
|
2883
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, SYSTEM_ONE_OPTION_KEYS, "SystemOneJudge", "SystemOneJudgeOptions");
|
|
2807
2884
|
super({
|
|
2808
2885
|
...options,
|
|
2809
2886
|
path: SYSTEM_ONE_PATH,
|
|
@@ -2903,6 +2980,7 @@ var SystemOneJudge = class extends AgentJudge {
|
|
|
2903
2980
|
* ```
|
|
2904
2981
|
*/
|
|
2905
2982
|
function createRelay(options) {
|
|
2983
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, RELAY_OPTION_KEYS, "createRelay", "RelayOptions");
|
|
2906
2984
|
const { provider, authorize, limit } = options;
|
|
2907
2985
|
const system = { ...options.system };
|
|
2908
2986
|
const { definitions, accept } = options.tools ?? {};
|
|
@@ -3057,6 +3135,43 @@ var DEFAULT_CONVERSATION_KEEP = 0;
|
|
|
3057
3135
|
* because it interpolates the per-call provenance `label`).
|
|
3058
3136
|
*/
|
|
3059
3137
|
var CONVERSATION_RECAP_PREFIX = "[Summary of earlier messages] ";
|
|
3138
|
+
/** Lists the members `ConversationOptions` declares, frozen — the keys the `Conversation` constructor admits. */
|
|
3139
|
+
var CONVERSATION_OPTION_KEYS = Object.freeze([
|
|
3140
|
+
"id",
|
|
3141
|
+
"on",
|
|
3142
|
+
"error",
|
|
3143
|
+
"summarize",
|
|
3144
|
+
"keep",
|
|
3145
|
+
"sections",
|
|
3146
|
+
"cumulative",
|
|
3147
|
+
"snapshot"
|
|
3148
|
+
]);
|
|
3149
|
+
/** Lists the members `ConversationManagerOptions` declares, frozen — the keys the `ConversationManager` constructor admits. */
|
|
3150
|
+
var CONVERSATION_MANAGER_KEYS = Object.freeze([
|
|
3151
|
+
"summarize",
|
|
3152
|
+
"keep",
|
|
3153
|
+
"sections",
|
|
3154
|
+
"store"
|
|
3155
|
+
]);
|
|
3156
|
+
/** Lists the members `ConversationInput` declares, frozen — the keys the `add` method of a `ConversationManager` admits. */
|
|
3157
|
+
var CONVERSATION_INPUT_KEYS = Object.freeze([
|
|
3158
|
+
"id",
|
|
3159
|
+
"summarize",
|
|
3160
|
+
"keep",
|
|
3161
|
+
"sections",
|
|
3162
|
+
"cumulative",
|
|
3163
|
+
"on",
|
|
3164
|
+
"error",
|
|
3165
|
+
"snapshot"
|
|
3166
|
+
]);
|
|
3167
|
+
/** Lists the members `CompactOptions` declares, frozen — the keys the `compact` method of a `Conversation` admits. */
|
|
3168
|
+
var COMPACT_OPTION_KEYS = Object.freeze([
|
|
3169
|
+
"keep",
|
|
3170
|
+
"sections",
|
|
3171
|
+
"signal"
|
|
3172
|
+
]);
|
|
3173
|
+
/** Lists the members `ConversationReferenceOptions` declares, frozen — the keys the `reference` method of a `Conversation` admits. */
|
|
3174
|
+
var REFERENCE_OPTION_KEYS = Object.freeze(["label", "messages"]);
|
|
3060
3175
|
// src/core/conversations/errors.ts
|
|
3061
3176
|
/**
|
|
3062
3177
|
* Reports a conversation with no {@link ConversationSummaryHandler} to fold its messages with, a
|
|
@@ -3082,7 +3197,8 @@ var CONVERSATION_RECAP_PREFIX = "[Summary of earlier messages] ";
|
|
|
3082
3197
|
* `'SUMMARY'`. A stored message plays a `ConversationRole`: `add` refuses any other role with
|
|
3083
3198
|
* `'ROLE'` before it stores any message of the batch, a hydrating snapshot that holds one throws
|
|
3084
3199
|
* `'ROLE'`, and an agent's selection that holds one becomes a `'ROLE'` fault. `add` refuses an input
|
|
3085
|
-
*
|
|
3200
|
+
* that is not a plain record, before its role is read, or whose other members fail `isMessageInput`
|
|
3201
|
+
* with `'MESSAGE'`, its `context` holding the `faults`,
|
|
3086
3202
|
* before it stores any message of the batch. A hydrating snapshot that fails
|
|
3087
3203
|
* `isConversationSnapshot` or repeats an id, and a store row or written snapshot that does, throw
|
|
3088
3204
|
* `'SNAPSHOT'`; a judgment the guard admits but JSON cannot carry still throws `'JUDGMENT'`. A
|
|
@@ -3090,7 +3206,7 @@ var CONVERSATION_RECAP_PREFIX = "[Summary of earlier messages] ";
|
|
|
3090
3206
|
* {@link isConversationError} and branch on `error.code`.
|
|
3091
3207
|
*/
|
|
3092
3208
|
var ConversationError = class extends Error {
|
|
3093
|
-
/** Names the machine-readable condition — `'SUMMARIZER'`: a `compact()` with no summarizer; `'SECTIONS'`: a `sections` cap that is not a positive safe integer, or a cap combined with `cumulative`; `'JUDGMENT'`: a judgment or judge request that is not JSON; `'KEEP'`: a `keep` that is not a nonnegative safe integer; `'SUMMARY'`: a summarizer digest whose trimmed text is empty; `'ROLE'`: a stored or selected message whose role is not a `ConversationRole`; `'MESSAGE'`: a message input whose other members fail `isMessageInput`; `'SNAPSHOT'`: a snapshot that fails `isConversationSnapshot` or repeats an id; `'ID'`: a manager's `add` of an id it already holds. */
|
|
3209
|
+
/** Names the machine-readable condition — `'SUMMARIZER'`: a `compact()` with no summarizer; `'SECTIONS'`: a `sections` cap that is not a positive safe integer, or a cap combined with `cumulative`; `'JUDGMENT'`: a judgment or judge request that is not JSON; `'KEEP'`: a `keep` that is not a nonnegative safe integer; `'SUMMARY'`: a summarizer digest whose trimmed text is empty; `'ROLE'`: a stored or selected message whose role is not a `ConversationRole`; `'MESSAGE'`: a message input that is not a plain record or whose other members fail `isMessageInput`; `'SNAPSHOT'`: a snapshot that fails `isConversationSnapshot` or repeats an id; `'ID'`: a manager's `add` of an id it already holds. */
|
|
3094
3210
|
code;
|
|
3095
3211
|
/** Holds the values that locate the failure, such as the `question` id of a malformed question, or `undefined` when none were supplied. */
|
|
3096
3212
|
context;
|
|
@@ -3225,13 +3341,17 @@ function isMessage(value) {
|
|
|
3225
3341
|
return checked.success && checked.value;
|
|
3226
3342
|
}
|
|
3227
3343
|
/**
|
|
3228
|
-
* Checks whether a value is a message input a conversation can store: a
|
|
3229
|
-
* `call`, `images`, and `thinking` absent or of their declared types.
|
|
3344
|
+
* Checks whether a value is a message input a conversation can store: a `ConversationRole`, a string
|
|
3345
|
+
* `content`, and `calls`, `call`, `images`, and `thinking` absent or of their declared types.
|
|
3230
3346
|
*
|
|
3231
3347
|
* @remarks
|
|
3232
|
-
*
|
|
3233
|
-
*
|
|
3234
|
-
*
|
|
3348
|
+
* An exact record, because a stored message is one: a value that is not a plain record, such as a
|
|
3349
|
+
* class instance or an object that inherits its `role`, returns false through `recordOf`'s own plain
|
|
3350
|
+
* record check, as does a member `MessageInput` does not declare or a declared optional member holding
|
|
3351
|
+
* `undefined`. Each tool call crosses `isToolCall` of
|
|
3352
|
+
* `@orkestrel/tool`, whose interface admits more members, so the store keeps `id`, `name`, and
|
|
3353
|
+
* `arguments` and leaves the rest. `add` reads the role through `requireRole` first, so its text can
|
|
3354
|
+
* name the role received. An unreadable member returns false.
|
|
3235
3355
|
*
|
|
3236
3356
|
* @param value - The unknown input candidate
|
|
3237
3357
|
* @returns True if the input's members are valid; false otherwise
|
|
@@ -3241,14 +3361,19 @@ function isMessage(value) {
|
|
|
3241
3361
|
* isMessageInput({ role: 'user' }) // false
|
|
3242
3362
|
* ```
|
|
3243
3363
|
*/
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
}
|
|
3364
|
+
var isMessageInput = (0, _orkestrel_contract.recordOf)({
|
|
3365
|
+
role: (0, _orkestrel_contract.literalOf)(CONVERSATION_ROLES),
|
|
3366
|
+
content: _orkestrel_contract.isString,
|
|
3367
|
+
calls: (0, _orkestrel_contract.arrayOf)(_orkestrel_tool.isToolCall),
|
|
3368
|
+
call: _orkestrel_contract.isString,
|
|
3369
|
+
images: (0, _orkestrel_contract.arrayOf)(_orkestrel_contract.isString),
|
|
3370
|
+
thinking: _orkestrel_contract.isString
|
|
3371
|
+
}, [
|
|
3372
|
+
"calls",
|
|
3373
|
+
"call",
|
|
3374
|
+
"images",
|
|
3375
|
+
"thinking"
|
|
3376
|
+
]);
|
|
3252
3377
|
/**
|
|
3253
3378
|
* Checks whether a stored judgment carries a valid question and exactly one answer or refusal.
|
|
3254
3379
|
*
|
|
@@ -3560,14 +3685,17 @@ function requireRole(role) {
|
|
|
3560
3685
|
* Returns a message input after checking its role and its members, or throws a coded error.
|
|
3561
3686
|
*
|
|
3562
3687
|
* @remarks
|
|
3563
|
-
*
|
|
3564
|
-
*
|
|
3565
|
-
*
|
|
3688
|
+
* A value that is not a plain record, such as a class instance or an object that inherits its
|
|
3689
|
+
* `role`, refuses first, through `isRecord` of `@orkestrel/contract`, with a text saying so. The role
|
|
3690
|
+
* goes next, through {@link requireRole}, so its refusal names what it received. The members
|
|
3691
|
+
* cross {@link isMessageInput}, an exact record guard, so a member `MessageInput` does not declare or a
|
|
3692
|
+
* declared member holding `undefined` refuses, and `audit` runs only after a refusal. Tool-call
|
|
3693
|
+
* arguments must also be JSON values before a message is stored.
|
|
3566
3694
|
*
|
|
3567
3695
|
* @param input - The message input to check
|
|
3568
3696
|
* @param contract - The message input contract, whose `audit` is read only on a refusal
|
|
3569
3697
|
* @returns The input, narrowed
|
|
3570
|
-
* @throws ConversationError Thrown when the role is not a `ConversationRole` (code `'ROLE'`) or a member fails `isMessageInput
|
|
3698
|
+
* @throws ConversationError Thrown when the input is not a plain record (code `'MESSAGE'`), when the role is not a `ConversationRole` (code `'ROLE'`), or when a member fails `isMessageInput`, an undeclared member included (code `'MESSAGE'`), with the faults under `context.faults`
|
|
3571
3699
|
*
|
|
3572
3700
|
* @example
|
|
3573
3701
|
* ```ts
|
|
@@ -3576,10 +3704,11 @@ function requireRole(role) {
|
|
|
3576
3704
|
* ```
|
|
3577
3705
|
*/
|
|
3578
3706
|
function requireMessageInput(input, contract) {
|
|
3579
|
-
|
|
3707
|
+
if (!(0, _orkestrel_contract.isRecord)(input)) throw new ConversationError("MESSAGE", "a message input must be a plain record, an object literal or parsed JSON holding its role and content as its own members, not a class instance or an object that inherits them", { context: { faults: contract.audit(input) } });
|
|
3708
|
+
requireRole(input.role);
|
|
3580
3709
|
if (isMessageInput(input) && (input.calls === void 0 || input.calls.every((call) => (0, _orkestrel_contract.isJSONValue)(call.arguments)))) return input;
|
|
3581
3710
|
const faults = contract.audit(input);
|
|
3582
|
-
throw new ConversationError("MESSAGE", `a message input must carry string content,
|
|
3711
|
+
throw new ConversationError("MESSAGE", `a message input must carry string content, calls, call, images, and thinking of their declared types, and no other member\n${(0, _orkestrel_contract.describeFaults)(faults, contract.schema)}`, { context: { faults } });
|
|
3583
3712
|
}
|
|
3584
3713
|
/**
|
|
3585
3714
|
* Returns a conversation snapshot after checking its shape, its stored roles, and its ids, or
|
|
@@ -3957,6 +4086,7 @@ var Conversation = class {
|
|
|
3957
4086
|
#judgments;
|
|
3958
4087
|
#pending = /* @__PURE__ */ new Set();
|
|
3959
4088
|
constructor(options) {
|
|
4089
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, CONVERSATION_OPTION_KEYS, "Conversation", "ConversationOptions");
|
|
3960
4090
|
const supplied = options?.snapshot;
|
|
3961
4091
|
const snapshot = supplied === void 0 ? void 0 : requireConversationSnapshot(supplied, conversationSnapshotContract);
|
|
3962
4092
|
this.#judgments = new JudgmentManager(snapshot?.judgments);
|
|
@@ -4012,6 +4142,7 @@ var Conversation = class {
|
|
|
4012
4142
|
return [...this.#sections.map((section) => buildRecapMessage(section)), ...this.#messages.values()];
|
|
4013
4143
|
}
|
|
4014
4144
|
async compact(options) {
|
|
4145
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, COMPACT_OPTION_KEYS, "Conversation", "CompactOptions");
|
|
4015
4146
|
const summarize = this.#summarize;
|
|
4016
4147
|
if (summarize === void 0) throw new ConversationError("SUMMARIZER", "cannot compact a conversation without a summarizer");
|
|
4017
4148
|
const cap = requireSectionsCap(options?.sections ?? this.#cap);
|
|
@@ -4036,6 +4167,7 @@ var Conversation = class {
|
|
|
4036
4167
|
return [...this.#sections.flatMap((section) => section.messages), ...this.#messages.values()].filter((message) => message.content.toLowerCase().includes(needle));
|
|
4037
4168
|
}
|
|
4038
4169
|
reference(options) {
|
|
4170
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, REFERENCE_OPTION_KEYS, "Conversation", "ConversationReferenceOptions");
|
|
4039
4171
|
const label = options?.label ?? this.#id;
|
|
4040
4172
|
const messages = options?.messages ?? [];
|
|
4041
4173
|
return [`[Reference — conversation "${label}" — NOT part of this conversation]`, ...messages.length > 0 ? ["Relevant messages:", ...Array.from(messages, (message) => `- ${message.role}: ${message.content}`)] : []].join("\n");
|
|
@@ -4162,6 +4294,7 @@ var ConversationManager = class {
|
|
|
4162
4294
|
#sections;
|
|
4163
4295
|
#store;
|
|
4164
4296
|
constructor(options) {
|
|
4297
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, CONVERSATION_MANAGER_KEYS, "ConversationManager", "ConversationManagerOptions");
|
|
4165
4298
|
this.#summarize = options?.summarize;
|
|
4166
4299
|
this.#keep = requireKeep(options?.keep ?? 0);
|
|
4167
4300
|
this.#sections = options?.sections;
|
|
@@ -4180,6 +4313,7 @@ var ConversationManager = class {
|
|
|
4180
4313
|
return [...this.#conversations.values()];
|
|
4181
4314
|
}
|
|
4182
4315
|
add(input) {
|
|
4316
|
+
(0, _orkestrel_contract.refuseOptionKeys)(input, CONVERSATION_INPUT_KEYS, "ConversationManager", "ConversationInput");
|
|
4183
4317
|
const held = input?.id ?? input?.snapshot?.id;
|
|
4184
4318
|
if (held !== void 0 && this.#conversations.has(held)) throw new ConversationError("ID", `conversation ${(0, _orkestrel_contract.preview)(held)} is already live; pick another id, or remove it first`, { context: { conversation: held } });
|
|
4185
4319
|
const sections = input?.sections ?? this.#sections;
|
|
@@ -4643,6 +4777,16 @@ function createDatabaseConversationStore(driver = (0, _orkestrel_database.create
|
|
|
4643
4777
|
*/
|
|
4644
4778
|
var WORKSPACE_SECTION_HEADER = "## Workspace";
|
|
4645
4779
|
/**
|
|
4780
|
+
* Names the section header the loaded skills' bodies render under at the end of the system
|
|
4781
|
+
* message — `'## Loaded skills'`.
|
|
4782
|
+
*/
|
|
4783
|
+
var LOADED_SECTION_HEADER = "## Loaded skills";
|
|
4784
|
+
/**
|
|
4785
|
+
* Names the section header the admitted workspace ids render under in the user context message —
|
|
4786
|
+
* `'## Workspaces'`, rendered only while a run can select a workspace.
|
|
4787
|
+
*/
|
|
4788
|
+
var WORKSPACE_INDEX_HEADER = "## Workspaces";
|
|
4789
|
+
/**
|
|
4646
4790
|
* Names the built-in section header a {@link import('./types.js').MemoManagerInterface} renders
|
|
4647
4791
|
* its index under — `'## Memos'`, the `open` value when no `format.open` overrides it.
|
|
4648
4792
|
*
|
|
@@ -4660,6 +4804,80 @@ var MEMO_SECTION_HEADER = "## Memos";
|
|
|
4660
4804
|
* {@link import('./helpers.js').renderIndexEntry}.
|
|
4661
4805
|
*/
|
|
4662
4806
|
var SKILL_SECTION_HEADER = "## Skills";
|
|
4807
|
+
/**
|
|
4808
|
+
* Lists the members `ScopeInput` declares, frozen — the keys a `Scope` is built from.
|
|
4809
|
+
*
|
|
4810
|
+
* @remarks
|
|
4811
|
+
* The `Scope` constructor passes the list to `refuseOptionKeys` of `@orkestrel/contract`, so an
|
|
4812
|
+
* own enumerable member outside it, a misspelled list included, refuses with a `ContractError`
|
|
4813
|
+
* coded `'structure'` instead of building a scope with no constraint.
|
|
4814
|
+
*/
|
|
4815
|
+
var SCOPE_KEYS = Object.freeze([
|
|
4816
|
+
"name",
|
|
4817
|
+
"select",
|
|
4818
|
+
"instructions",
|
|
4819
|
+
"skills",
|
|
4820
|
+
"memos",
|
|
4821
|
+
"tools",
|
|
4822
|
+
"files",
|
|
4823
|
+
"workspaces"
|
|
4824
|
+
]);
|
|
4825
|
+
/**
|
|
4826
|
+
* Lists the members `ScopeFilter` declares, frozen — the keys the `narrow` method of a scope admits.
|
|
4827
|
+
*
|
|
4828
|
+
* @remarks
|
|
4829
|
+
* The `narrow` method passes the list to `refuseOptionKeys` of `@orkestrel/contract`, so an own
|
|
4830
|
+
* enumerable member outside it refuses with a `ContractError` coded `'structure'`.
|
|
4831
|
+
*/
|
|
4832
|
+
var SCOPE_FILTER_KEYS = Object.freeze([
|
|
4833
|
+
"instructions",
|
|
4834
|
+
"skills",
|
|
4835
|
+
"memos",
|
|
4836
|
+
"tools",
|
|
4837
|
+
"files",
|
|
4838
|
+
"workspaces"
|
|
4839
|
+
]);
|
|
4840
|
+
/** Lists the members `AgentContextOptions` declares, frozen — the keys the `AgentContext` constructor admits. */
|
|
4841
|
+
var CONTEXT_OPTION_KEYS = Object.freeze([
|
|
4842
|
+
"system",
|
|
4843
|
+
"tools",
|
|
4844
|
+
"instructions",
|
|
4845
|
+
"skills",
|
|
4846
|
+
"memos",
|
|
4847
|
+
"workspaces",
|
|
4848
|
+
"scope",
|
|
4849
|
+
"conversations",
|
|
4850
|
+
"select"
|
|
4851
|
+
]);
|
|
4852
|
+
/** Lists the members `ContextBuildOptions` declares, frozen — the keys the `build` method of an `AgentContext` admits. */
|
|
4853
|
+
var BUILD_OPTION_KEYS = Object.freeze([
|
|
4854
|
+
"selection",
|
|
4855
|
+
"scope",
|
|
4856
|
+
"disclosure",
|
|
4857
|
+
"switchable"
|
|
4858
|
+
]);
|
|
4859
|
+
/** Lists the members `InstructionManagerOptions` declares, frozen — the keys the `InstructionManager` constructor admits. */
|
|
4860
|
+
var INSTRUCTION_MANAGER_KEYS = Object.freeze([
|
|
4861
|
+
"on",
|
|
4862
|
+
"error",
|
|
4863
|
+
"format"
|
|
4864
|
+
]);
|
|
4865
|
+
/** Lists the members `MemoManagerOptions` declares, frozen — the keys the `MemoManager` constructor admits. */
|
|
4866
|
+
var MEMO_MANAGER_KEYS = Object.freeze([
|
|
4867
|
+
"id",
|
|
4868
|
+
"on",
|
|
4869
|
+
"error",
|
|
4870
|
+
"format",
|
|
4871
|
+
"snapshot"
|
|
4872
|
+
]);
|
|
4873
|
+
/** Lists the members `ScopeManagerOptions` declares, frozen — the keys the `ScopeManager` constructor admits. */
|
|
4874
|
+
var SCOPE_MANAGER_KEYS = Object.freeze(["on", "error"]);
|
|
4875
|
+
/** Lists the members `SkillManagerOptions` declares, frozen — the keys the `SkillManager` constructor admits. */
|
|
4876
|
+
var SKILL_MANAGER_KEYS = Object.freeze([
|
|
4877
|
+
"on",
|
|
4878
|
+
"error",
|
|
4879
|
+
"format"
|
|
4880
|
+
]);
|
|
4663
4881
|
// src/core/contexts/errors.ts
|
|
4664
4882
|
/**
|
|
4665
4883
|
* Reports a context snapshot that `isMemoSnapshot` refuses, thrown by the construction of a
|
|
@@ -4721,132 +4939,84 @@ function isContextError(value) {
|
|
|
4721
4939
|
return context === void 0 || (0, _orkestrel_contract.isObject)(context) || (0, _orkestrel_contract.isFunction)(context);
|
|
4722
4940
|
});
|
|
4723
4941
|
}
|
|
4724
|
-
// src/core/contexts/
|
|
4942
|
+
// src/core/contexts/shapers.ts
|
|
4943
|
+
/** Describes a stored memo revision exactly. */
|
|
4944
|
+
var memoShape = (0, _orkestrel_contract.objectShape)({
|
|
4945
|
+
id: (0, _orkestrel_contract.stringShape)(),
|
|
4946
|
+
name: (0, _orkestrel_contract.stringShape)(),
|
|
4947
|
+
description: (0, _orkestrel_contract.stringShape)(),
|
|
4948
|
+
content: (0, _orkestrel_contract.stringShape)(),
|
|
4949
|
+
time: (0, _orkestrel_contract.numberShape)(),
|
|
4950
|
+
origin: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.objectShape)({
|
|
4951
|
+
conversation: (0, _orkestrel_contract.stringShape)(),
|
|
4952
|
+
message: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)())
|
|
4953
|
+
}))
|
|
4954
|
+
});
|
|
4725
4955
|
/**
|
|
4726
|
-
*
|
|
4727
|
-
* its input (`name` / `content` and an optional `priority`), the `id` minted at
|
|
4728
|
-
* construction. Default: `0` for `priority`.
|
|
4956
|
+
* Describes a stored memo set exactly: an unknown key refuses.
|
|
4729
4957
|
*
|
|
4730
4958
|
* @remarks
|
|
4731
|
-
*
|
|
4732
|
-
*
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4959
|
+
* `isMemoSnapshot` is the door a hydrate and a store read, and this shape fills a refusal's faults. A
|
|
4960
|
+
* lockstep test holds that `is` implies the guard, and on the snapshot fixtures the converse.
|
|
4961
|
+
*/
|
|
4962
|
+
var memoSnapshotShape = (0, _orkestrel_contract.objectShape)({
|
|
4963
|
+
id: (0, _orkestrel_contract.stringShape)(),
|
|
4964
|
+
memos: (0, _orkestrel_contract.arrayShape)(memoShape)
|
|
4965
|
+
});
|
|
4966
|
+
/**
|
|
4967
|
+
* Describes a memo input exactly: the members of a stored revision less the `id` and `time` the
|
|
4968
|
+
* manager mints, each taken from {@link memoShape}.
|
|
4738
4969
|
*
|
|
4739
|
-
* @
|
|
4740
|
-
*
|
|
4741
|
-
*
|
|
4742
|
-
* instruction.priority // 5
|
|
4743
|
-
* ```
|
|
4970
|
+
* @remarks
|
|
4971
|
+
* An unknown member, a missing required member, and a declared member holding `undefined` refuse, so
|
|
4972
|
+
* a revision `add` stores is one its own snapshot hydrates.
|
|
4744
4973
|
*/
|
|
4745
|
-
var
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
content
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
constructor(input) {
|
|
4752
|
-
this.id = crypto.randomUUID();
|
|
4753
|
-
this.name = input.name;
|
|
4754
|
-
this.content = input.content;
|
|
4755
|
-
this.priority = input.priority ?? 0;
|
|
4756
|
-
if (input.override !== void 0) this.override = input.override;
|
|
4757
|
-
}
|
|
4758
|
-
};
|
|
4759
|
-
// src/core/contexts/instructions/InstructionManager.ts
|
|
4974
|
+
var memoInputShape = (0, _orkestrel_contract.objectShape)({
|
|
4975
|
+
name: memoShape.properties.name,
|
|
4976
|
+
description: memoShape.properties.description,
|
|
4977
|
+
content: memoShape.properties.content,
|
|
4978
|
+
origin: memoShape.properties.origin
|
|
4979
|
+
});
|
|
4760
4980
|
/**
|
|
4761
|
-
*
|
|
4762
|
-
*
|
|
4763
|
-
* last write wins, and listed by descending `priority`, carrying the `open` / `render` / `close` build contract and an observable `emitter`.
|
|
4981
|
+
* Describes an instruction input exactly: a string `name` and `content`, and an optional finite
|
|
4982
|
+
* `priority` and string `override`.
|
|
4764
4983
|
*
|
|
4765
4984
|
* @remarks
|
|
4766
|
-
*
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
*
|
|
4776
|
-
*
|
|
4777
|
-
* `undefined`. A context reads the three and frames the section from them (see
|
|
4778
|
-
* {@link import('../AgentContext.js').AgentContext}).
|
|
4779
|
-
* - **Removal.** `remove` drops one by name, or a batch — `true` only when every supplied
|
|
4780
|
-
* name was removed; `clear` empties the registry.
|
|
4781
|
-
* - **Observable.** The owned {@link emitter} ({@link InstructionManagerEventMap})
|
|
4782
|
-
* carries `add` (the created instruction) / `remove` (the name) / `clear` for
|
|
4783
|
-
* fire-and-forget observers. Every event is emitted directly, strictly after the map
|
|
4784
|
-
* mutation completes; the emitter isolates a listener throw and routes it to its `error`
|
|
4785
|
-
* handler (the `error` option), so a buggy observer can never corrupt a mutation.
|
|
4985
|
+
* An unknown member, a missing required member, and a declared member holding `undefined` refuse.
|
|
4986
|
+
*/
|
|
4987
|
+
var instructionInputShape = (0, _orkestrel_contract.objectShape)({
|
|
4988
|
+
name: (0, _orkestrel_contract.stringShape)(),
|
|
4989
|
+
content: (0, _orkestrel_contract.stringShape)(),
|
|
4990
|
+
priority: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.numberShape)()),
|
|
4991
|
+
override: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)())
|
|
4992
|
+
});
|
|
4993
|
+
/**
|
|
4994
|
+
* Describes a skill input exactly: a string `name`, `description`, and `content`, optional `tools`
|
|
4995
|
+
* names, and optional `files`.
|
|
4786
4996
|
*
|
|
4787
|
-
* @
|
|
4788
|
-
*
|
|
4789
|
-
*
|
|
4790
|
-
*
|
|
4791
|
-
* { name: 'tone', content: 'Be concise.', priority: 1 },
|
|
4792
|
-
* { name: 'safety', content: 'Refuse unsafe requests.', priority: 10 },
|
|
4793
|
-
* ])
|
|
4794
|
-
* manager.instructions().map((one) => one.name) // ['safety', 'tone'] — highest priority first
|
|
4795
|
-
* ```
|
|
4997
|
+
* @remarks
|
|
4998
|
+
* An unknown member, a missing required member, and a declared member holding `undefined` refuse.
|
|
4999
|
+
* Each file is the `FileInput` interface of `@orkestrel/workspace`, which publishes no guard for it,
|
|
5000
|
+
* so the shape admits any defined element and `createFile`, that package's own door, reads it.
|
|
4796
5001
|
*/
|
|
4797
|
-
var
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
return this.#format?.close;
|
|
4816
|
-
}
|
|
4817
|
-
add(input) {
|
|
4818
|
-
if ((0, _orkestrel_contract.isArray)(input)) return input.map((one) => this.#create(one));
|
|
4819
|
-
return this.#create(input);
|
|
4820
|
-
}
|
|
4821
|
-
instruction(name) {
|
|
4822
|
-
return this.#instructions.get(name);
|
|
4823
|
-
}
|
|
4824
|
-
instructions() {
|
|
4825
|
-
return [...this.#instructions.values()].sort((a, b) => b.priority - a.priority);
|
|
4826
|
-
}
|
|
4827
|
-
render(instruction) {
|
|
4828
|
-
return instruction.override ?? this.#format?.render?.(instruction) ?? instruction.content;
|
|
4829
|
-
}
|
|
4830
|
-
remove(names) {
|
|
4831
|
-
if ((0, _orkestrel_contract.isArray)(names)) return removeEntries(names, (name) => this.#delete(name));
|
|
4832
|
-
return this.#delete(names);
|
|
4833
|
-
}
|
|
4834
|
-
clear() {
|
|
4835
|
-
this.#instructions.clear();
|
|
4836
|
-
this.#emitter.emit("clear");
|
|
4837
|
-
}
|
|
4838
|
-
#create(input) {
|
|
4839
|
-
const instruction = new Instruction(input);
|
|
4840
|
-
this.#instructions.set(instruction.name, instruction);
|
|
4841
|
-
this.#emitter.emit("add", instruction);
|
|
4842
|
-
return instruction;
|
|
4843
|
-
}
|
|
4844
|
-
#delete(name) {
|
|
4845
|
-
const removed = this.#instructions.delete(name);
|
|
4846
|
-
if (removed) this.#emitter.emit("remove", name);
|
|
4847
|
-
return removed;
|
|
4848
|
-
}
|
|
4849
|
-
};
|
|
5002
|
+
var skillInputShape = (0, _orkestrel_contract.objectShape)({
|
|
5003
|
+
name: (0, _orkestrel_contract.stringShape)(),
|
|
5004
|
+
description: (0, _orkestrel_contract.stringShape)(),
|
|
5005
|
+
content: (0, _orkestrel_contract.stringShape)(),
|
|
5006
|
+
tools: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)((0, _orkestrel_contract.stringShape)())),
|
|
5007
|
+
files: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)((0, _orkestrel_contract.rawShape)({})))
|
|
5008
|
+
});
|
|
5009
|
+
// src/core/contexts/contracts.ts
|
|
5010
|
+
/** Reads the faults of a refused memo revision. */
|
|
5011
|
+
var memoContract = (0, _orkestrel_contract.createContract)(memoShape);
|
|
5012
|
+
/** Reads the faults of a refused memo snapshot. */
|
|
5013
|
+
var memoSnapshotContract = (0, _orkestrel_contract.createContract)(memoSnapshotShape);
|
|
5014
|
+
/** Admits a memo input exactly, and reads the faults of a refused one. */
|
|
5015
|
+
var memoInputContract = (0, _orkestrel_contract.createContract)(memoInputShape);
|
|
5016
|
+
/** Admits an instruction input exactly, and reads the faults of a refused one. */
|
|
5017
|
+
var instructionInputContract = (0, _orkestrel_contract.createContract)(instructionInputShape);
|
|
5018
|
+
/** Admits a skill input exactly, and reads the faults of a refused one. */
|
|
5019
|
+
var skillInputContract = (0, _orkestrel_contract.createContract)(skillInputShape);
|
|
4850
5020
|
// src/core/contexts/helpers.ts
|
|
4851
5021
|
/**
|
|
4852
5022
|
* Renders a path-addressed text body as a fenced reference block — a `File: <path>` label line
|
|
@@ -5020,6 +5190,70 @@ function intersectKeys(parent, child) {
|
|
|
5020
5190
|
return child.filter((key) => allowed.has(key));
|
|
5021
5191
|
}
|
|
5022
5192
|
/**
|
|
5193
|
+
* Intersects two scope filters list by list; an absent side constrains nothing.
|
|
5194
|
+
*
|
|
5195
|
+
* @remarks
|
|
5196
|
+
* Pure, and it can only tighten. Each list of the result is {@link intersectKeys} over the two
|
|
5197
|
+
* sides' lists, each read one time, in the order `instructions`, `skills`, `memos`, `tools`,
|
|
5198
|
+
* `files`, and `workspaces`. A list neither side constrains is omitted, and every returned list is
|
|
5199
|
+
* a fresh copy.
|
|
5200
|
+
* `Scope.narrow`, a run's build, and a ledger's answer-pass scope compose their layers through it.
|
|
5201
|
+
*
|
|
5202
|
+
* @param parent - The outer filter, such as the context scope (`undefined` ⇒ no constraint)
|
|
5203
|
+
* @param child - The inner filter, such as a run scope (`undefined` ⇒ no constraint)
|
|
5204
|
+
* @returns The intersected filter
|
|
5205
|
+
*
|
|
5206
|
+
* @example
|
|
5207
|
+
* ```ts
|
|
5208
|
+
* import { intersectFilters } from '@orkestrel/agent'
|
|
5209
|
+
*
|
|
5210
|
+
* intersectFilters({ tools: ['read', 'write'] }, { tools: ['write', 'admin'], files: [] })
|
|
5211
|
+
* // { tools: ['write'], files: [] }
|
|
5212
|
+
* ```
|
|
5213
|
+
*/
|
|
5214
|
+
function intersectFilters(parent, child) {
|
|
5215
|
+
const instructions = intersectKeys(parent?.instructions, child?.instructions);
|
|
5216
|
+
const skills = intersectKeys(parent?.skills, child?.skills);
|
|
5217
|
+
const memos = intersectKeys(parent?.memos, child?.memos);
|
|
5218
|
+
const tools = intersectKeys(parent?.tools, child?.tools);
|
|
5219
|
+
const files = intersectKeys(parent?.files, child?.files);
|
|
5220
|
+
const workspaces = intersectKeys(parent?.workspaces, child?.workspaces);
|
|
5221
|
+
return {
|
|
5222
|
+
...instructions === void 0 ? {} : { instructions },
|
|
5223
|
+
...skills === void 0 ? {} : { skills },
|
|
5224
|
+
...memos === void 0 ? {} : { memos },
|
|
5225
|
+
...tools === void 0 ? {} : { tools },
|
|
5226
|
+
...files === void 0 ? {} : { files },
|
|
5227
|
+
...workspaces === void 0 ? {} : { workspaces }
|
|
5228
|
+
};
|
|
5229
|
+
}
|
|
5230
|
+
/**
|
|
5231
|
+
* Resolves the workspace a run renders and acts on: the one `selected` names, else the registry's
|
|
5232
|
+
* active one, kept only while `allowed` admits its id.
|
|
5233
|
+
*
|
|
5234
|
+
* @remarks
|
|
5235
|
+
* A selection the registry does not hold resolves to `undefined` and never falls back to the active
|
|
5236
|
+
* workspace. `allowed` is three-way, as {@link import('../helpers.js').filterAllowList} reads it.
|
|
5237
|
+
* `AgentContext.build`, the caller an agent run hands each tool call, and a ledger's calibration
|
|
5238
|
+
* resolve through it, so what a request renders and what a call receives come from one rule.
|
|
5239
|
+
*
|
|
5240
|
+
* @param workspaces - The workspace registry to read
|
|
5241
|
+
* @param selected - The id a `switch` call or the `workspace` option selected, or `undefined`
|
|
5242
|
+
* @param allowed - The ceiling's `workspaces` list (`undefined` ⇒ every id, `[]` ⇒ none)
|
|
5243
|
+
* @returns The workspace, or `undefined` when none resolves
|
|
5244
|
+
*
|
|
5245
|
+
* @example
|
|
5246
|
+
* ```ts
|
|
5247
|
+
* import { resolveWorkspace } from '@orkestrel/agent'
|
|
5248
|
+
*
|
|
5249
|
+
* resolveWorkspace(agent.context.workspaces, 'returns', ['returns']) // the registered returns workspace
|
|
5250
|
+
* ```
|
|
5251
|
+
*/
|
|
5252
|
+
function resolveWorkspace(workspaces, selected, allowed) {
|
|
5253
|
+
const workspace = selected === void 0 ? workspaces.active : workspaces.workspace(selected);
|
|
5254
|
+
return workspace !== void 0 && filterAllowList(allowed, [workspace.id], (id) => id).length > 0 ? workspace : void 0;
|
|
5255
|
+
}
|
|
5256
|
+
/**
|
|
5023
5257
|
* Renders one index line for a memo or a skill — `- name: description`, the built-in `render` of
|
|
5024
5258
|
* the memo and skill managers.
|
|
5025
5259
|
*
|
|
@@ -5039,37 +5273,219 @@ function intersectKeys(parent, child) {
|
|
|
5039
5273
|
* ```
|
|
5040
5274
|
*/
|
|
5041
5275
|
function renderIndexEntry(entry) {
|
|
5042
|
-
return
|
|
5276
|
+
return renderIndexLine(`${entry.name}: ${entry.description}`);
|
|
5043
5277
|
}
|
|
5044
|
-
// src/core/contexts/shapers.ts
|
|
5045
|
-
/** Describes a stored memo revision exactly. */
|
|
5046
|
-
var memoShape = (0, _orkestrel_contract.objectShape)({
|
|
5047
|
-
id: (0, _orkestrel_contract.stringShape)(),
|
|
5048
|
-
name: (0, _orkestrel_contract.stringShape)(),
|
|
5049
|
-
description: (0, _orkestrel_contract.stringShape)(),
|
|
5050
|
-
content: (0, _orkestrel_contract.stringShape)(),
|
|
5051
|
-
time: (0, _orkestrel_contract.numberShape)(),
|
|
5052
|
-
origin: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.objectShape)({
|
|
5053
|
-
conversation: (0, _orkestrel_contract.stringShape)(),
|
|
5054
|
-
message: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)())
|
|
5055
|
-
}))
|
|
5056
|
-
});
|
|
5057
5278
|
/**
|
|
5058
|
-
*
|
|
5279
|
+
* Renders one index line from its text as `- text`, with each run of line-break code points as one
|
|
5280
|
+
* space.
|
|
5059
5281
|
*
|
|
5060
5282
|
* @remarks
|
|
5061
|
-
*
|
|
5062
|
-
*
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5283
|
+
* Pure and total. The memo and skill index lines and the workspace index lines render through it,
|
|
5284
|
+
* so text in an index never opens another line or heading.
|
|
5285
|
+
*
|
|
5286
|
+
* @param text - The line's text
|
|
5287
|
+
* @returns The index line
|
|
5288
|
+
*
|
|
5289
|
+
* @example
|
|
5290
|
+
* ```ts
|
|
5291
|
+
* import { renderIndexLine } from '@orkestrel/agent'
|
|
5292
|
+
*
|
|
5293
|
+
* renderIndexLine('orders') // '- orders'
|
|
5294
|
+
* ```
|
|
5295
|
+
*/
|
|
5296
|
+
function renderIndexLine(text) {
|
|
5297
|
+
return `- ${text}`.replace(/[\n\v\f\r\u0085\u2028\u2029]+/g, " ");
|
|
5298
|
+
}
|
|
5299
|
+
/**
|
|
5300
|
+
* Renders one loaded skill's body: `### name`, a blank line, then its content.
|
|
5301
|
+
*
|
|
5302
|
+
* @remarks
|
|
5303
|
+
* Pure and total. The context build renders each shown skill through it under the loaded skills
|
|
5304
|
+
* header, last in the system message.
|
|
5305
|
+
*
|
|
5306
|
+
* @param skill - The skill whose name and content the body shows
|
|
5307
|
+
* @returns The body
|
|
5308
|
+
*
|
|
5309
|
+
* @example
|
|
5310
|
+
* ```ts
|
|
5311
|
+
* import { renderSkillBody } from '@orkestrel/agent'
|
|
5312
|
+
*
|
|
5313
|
+
* renderSkillBody({ name: 'refunds', content: 'Check the order first.' })
|
|
5314
|
+
* // '### refunds\n\nCheck the order first.'
|
|
5315
|
+
* ```
|
|
5316
|
+
*/
|
|
5317
|
+
function renderSkillBody(skill) {
|
|
5318
|
+
return `### ${skill.name}\n\n${skill.content}`;
|
|
5319
|
+
}
|
|
5320
|
+
/**
|
|
5321
|
+
* Checks a stored record's input against its exact contract, and throws a `ContractError` coded
|
|
5322
|
+
* `'structure'` when it fails.
|
|
5323
|
+
*
|
|
5324
|
+
* @remarks
|
|
5325
|
+
* The input must pass the contract's `is` guard: a missing required member, a member the input type
|
|
5326
|
+
* does not declare, a declared member holding `undefined`, and a member of another type each refuse,
|
|
5327
|
+
* so nothing a refused input carries is stored. A class instance refuses, and the contract reads own
|
|
5328
|
+
* enumerable string keys alone, as every option door does. The contract's `audit` runs only after a refusal, and
|
|
5329
|
+
* the message names the owner, the input type, and every fault with the members to send. The
|
|
5330
|
+
* `context` holds the first fault's `path` and the input type as its `shape`.
|
|
5331
|
+
*
|
|
5332
|
+
* @param input - The input to check
|
|
5333
|
+
* @param contract - The input's compiled contract
|
|
5334
|
+
* @param owner - The entity named at the head of the refusal
|
|
5335
|
+
* @param shape - The input type named in the refusal and its `context`
|
|
5336
|
+
* @throws ContractError Thrown when the input fails the contract (code `'structure'`)
|
|
5337
|
+
*
|
|
5338
|
+
* @example
|
|
5339
|
+
* ```ts
|
|
5340
|
+
* import { memoInputContract, requireRecordInput } from '@orkestrel/agent'
|
|
5341
|
+
*
|
|
5342
|
+
* requireRecordInput({ name: 'tone', description: 'Prefers short replies.', content: 'Answer briefly.' }, memoInputContract, 'MemoManager', 'MemoInput') // returns
|
|
5343
|
+
* requireRecordInput({ name: 'tone', description: undefined, content: 'Answer briefly.' }, memoInputContract, 'MemoManager', 'MemoInput') // throws ContractError ('structure')
|
|
5344
|
+
* ```
|
|
5345
|
+
*/
|
|
5346
|
+
function requireRecordInput(input, contract, owner, shape) {
|
|
5347
|
+
if (contract.is(input)) return;
|
|
5348
|
+
const faults = contract.audit(input);
|
|
5349
|
+
throw new _orkestrel_contract.ContractError(`${owner}: a ${shape} must hold each required member and no other, every member of its declared type\n${(0, _orkestrel_contract.describeFaults)(faults, contract.schema)}`, {
|
|
5350
|
+
code: "structure",
|
|
5351
|
+
context: {
|
|
5352
|
+
path: faults[0]?.path ?? [],
|
|
5353
|
+
shape
|
|
5354
|
+
}
|
|
5355
|
+
});
|
|
5356
|
+
}
|
|
5357
|
+
// src/core/contexts/instructions/Instruction.ts
|
|
5358
|
+
/**
|
|
5359
|
+
* Represents an immutable named directive — an {@link InstructionInterface} assembled once from
|
|
5360
|
+
* its input (`name` / `content` and an optional `priority`), the `id` minted at
|
|
5361
|
+
* construction. Default: `0` for `priority`.
|
|
5362
|
+
*
|
|
5363
|
+
* @remarks
|
|
5364
|
+
* A thin immutable value object (mirroring {@link import('@orkestrel/tool').Tool}): the
|
|
5365
|
+
* constructor mints a fresh `id` (`crypto.randomUUID()`), copies the input's `name` /
|
|
5366
|
+
* `content`, resolves `priority` to the input's value or `0`, and carries the input's
|
|
5367
|
+
* per-item `override` value when supplied; otherwise the instance owns no `override` member, so
|
|
5368
|
+
* its own keys and a JSON copy carry no member holding `undefined`. The input meets its exact
|
|
5369
|
+
* contract first, so a missing required member, an undeclared member, and a declared member holding
|
|
5370
|
+
* `undefined` refuse with a `ContractError` coded `'structure'`. Never mutated after construction. An
|
|
5371
|
+
* {@link import('./InstructionManager.js').InstructionManager} keys it by `name` and
|
|
5372
|
+
* renders it (highest `priority` first) under its section header.
|
|
5373
|
+
*
|
|
5374
|
+
* @example
|
|
5375
|
+
* ```ts
|
|
5376
|
+
* const instruction = new Instruction({ name: 'tone', content: 'Be concise.', priority: 5 })
|
|
5377
|
+
* instruction.priority // 5
|
|
5378
|
+
* ```
|
|
5379
|
+
*/
|
|
5380
|
+
var Instruction = class {
|
|
5381
|
+
id;
|
|
5382
|
+
name;
|
|
5383
|
+
content;
|
|
5384
|
+
priority;
|
|
5385
|
+
constructor(input) {
|
|
5386
|
+
requireRecordInput(input, instructionInputContract, "Instruction", "InstructionInput");
|
|
5387
|
+
this.id = crypto.randomUUID();
|
|
5388
|
+
this.name = input.name;
|
|
5389
|
+
this.content = input.content;
|
|
5390
|
+
this.priority = input.priority ?? 0;
|
|
5391
|
+
if (input.override !== void 0) this.override = input.override;
|
|
5392
|
+
}
|
|
5393
|
+
};
|
|
5394
|
+
// src/core/contexts/instructions/InstructionManager.ts
|
|
5395
|
+
/**
|
|
5396
|
+
* Registers the immutable {@link Instruction} instances a richer context assembles a directives block
|
|
5397
|
+
* from — keyed by `name` so adding an instruction whose name is already stored overwrites it,
|
|
5398
|
+
* last write wins, and listed by descending `priority`, carrying the `open` / `render` / `close` build contract and an observable `emitter`.
|
|
5399
|
+
*
|
|
5400
|
+
* @remarks
|
|
5401
|
+
* - **Registry.** Instructions live in an insertion-ordered `Map` keyed by `name`;
|
|
5402
|
+
* `add` takes one {@link InstructionInput} or a batch, mints each instruction's
|
|
5403
|
+
* `id`, and adding an instruction whose name is already stored overwrites it (last write wins).
|
|
5404
|
+
* `count` is the map size, `instruction(name)` looks one up, and `instructions()` lists them sorted by
|
|
5405
|
+
* descending `priority` (a stable sort, so equal priorities keep insertion order).
|
|
5406
|
+
* - **Build contract (the whole format cascade).** `open` is the section header a context
|
|
5407
|
+
* renders the instructions under, `render(instruction)` renders one instruction, and
|
|
5408
|
+
* `close` is the line after them. Each resolves the cascade most-specific-first: `render`
|
|
5409
|
+
* returns the instruction's {@link InstructionInput.override}, else the
|
|
5410
|
+
* `InstructionManagerOptions.format` `render`, else its `content`; `open` returns the
|
|
5411
|
+
* options `open`, else the built-in header; `close` returns the options `close`, else
|
|
5412
|
+
* `undefined`. A context reads the three and frames the section from them (see
|
|
5413
|
+
* {@link import('../AgentContext.js').AgentContext}).
|
|
5414
|
+
* - **Removal.** `remove` drops one by name, or a batch — `true` only when every supplied
|
|
5415
|
+
* name was removed; `clear` empties the registry.
|
|
5416
|
+
* - **Observable.** The owned {@link emitter} ({@link InstructionManagerEventMap})
|
|
5417
|
+
* carries `add` (the created instruction) / `remove` (the name) / `clear` for
|
|
5418
|
+
* fire-and-forget observers. Every event is emitted directly, strictly after the map
|
|
5419
|
+
* mutation completes; the emitter isolates a listener throw and routes it to its `error`
|
|
5420
|
+
* handler (the `error` option), so a buggy observer can never corrupt a mutation.
|
|
5421
|
+
*
|
|
5422
|
+
* @example
|
|
5423
|
+
* ```ts
|
|
5424
|
+
* const manager = new InstructionManager()
|
|
5425
|
+
* manager.add([
|
|
5426
|
+
* { name: 'tone', content: 'Be concise.', priority: 1 },
|
|
5427
|
+
* { name: 'safety', content: 'Refuse unsafe requests.', priority: 10 },
|
|
5428
|
+
* ])
|
|
5429
|
+
* manager.instructions().map((one) => one.name) // ['safety', 'tone'] — highest priority first
|
|
5430
|
+
* ```
|
|
5431
|
+
*/
|
|
5432
|
+
var InstructionManager = class {
|
|
5433
|
+
#instructions = /* @__PURE__ */ new Map();
|
|
5434
|
+
#emitter;
|
|
5435
|
+
#format;
|
|
5436
|
+
constructor(options) {
|
|
5437
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, INSTRUCTION_MANAGER_KEYS, "InstructionManager", "InstructionManagerOptions");
|
|
5438
|
+
this.#emitter = new _orkestrel_emitter.Emitter(options);
|
|
5439
|
+
this.#format = options?.format;
|
|
5440
|
+
}
|
|
5441
|
+
get emitter() {
|
|
5442
|
+
return this.#emitter;
|
|
5443
|
+
}
|
|
5444
|
+
get count() {
|
|
5445
|
+
return this.#instructions.size;
|
|
5446
|
+
}
|
|
5447
|
+
get open() {
|
|
5448
|
+
return this.#format?.open ?? "## Instructions";
|
|
5449
|
+
}
|
|
5450
|
+
get close() {
|
|
5451
|
+
return this.#format?.close;
|
|
5452
|
+
}
|
|
5453
|
+
add(input) {
|
|
5454
|
+
if ((0, _orkestrel_contract.isArray)(input)) {
|
|
5455
|
+
input.forEach((one) => requireRecordInput(one, instructionInputContract, "Instruction", "InstructionInput"));
|
|
5456
|
+
return input.map((one) => this.#create(one));
|
|
5457
|
+
}
|
|
5458
|
+
return this.#create(input);
|
|
5459
|
+
}
|
|
5460
|
+
instruction(name) {
|
|
5461
|
+
return this.#instructions.get(name);
|
|
5462
|
+
}
|
|
5463
|
+
instructions() {
|
|
5464
|
+
return [...this.#instructions.values()].sort((a, b) => b.priority - a.priority);
|
|
5465
|
+
}
|
|
5466
|
+
render(instruction) {
|
|
5467
|
+
return instruction.override ?? this.#format?.render?.(instruction) ?? instruction.content;
|
|
5468
|
+
}
|
|
5469
|
+
remove(names) {
|
|
5470
|
+
if ((0, _orkestrel_contract.isArray)(names)) return removeEntries(names, (name) => this.#delete(name));
|
|
5471
|
+
return this.#delete(names);
|
|
5472
|
+
}
|
|
5473
|
+
clear() {
|
|
5474
|
+
this.#instructions.clear();
|
|
5475
|
+
this.#emitter.emit("clear");
|
|
5476
|
+
}
|
|
5477
|
+
#create(input) {
|
|
5478
|
+
const instruction = new Instruction(input);
|
|
5479
|
+
this.#instructions.set(instruction.name, instruction);
|
|
5480
|
+
this.#emitter.emit("add", instruction);
|
|
5481
|
+
return instruction;
|
|
5482
|
+
}
|
|
5483
|
+
#delete(name) {
|
|
5484
|
+
const removed = this.#instructions.delete(name);
|
|
5485
|
+
if (removed) this.#emitter.emit("remove", name);
|
|
5486
|
+
return removed;
|
|
5487
|
+
}
|
|
5488
|
+
};
|
|
5073
5489
|
// src/core/contexts/validators.ts
|
|
5074
5490
|
/**
|
|
5075
5491
|
* Checks whether a value is a memo's origin: a record with a string `conversation` and an optional
|
|
@@ -5150,6 +5566,35 @@ function isMemoSnapshot(value) {
|
|
|
5150
5566
|
return (0, _orkestrel_contract.isString)(id) && (0, _orkestrel_contract.arrayOf)(isMemo)(memos);
|
|
5151
5567
|
});
|
|
5152
5568
|
}
|
|
5569
|
+
/**
|
|
5570
|
+
* Checks whether a value implements `ScopeInterface`; total, each member read one time, any
|
|
5571
|
+
* implementation admitted.
|
|
5572
|
+
*
|
|
5573
|
+
* @remarks
|
|
5574
|
+
* A scope is a string `id` and `name`, each of `instructions`, `skills`, `memos`, `tools`, `files`,
|
|
5575
|
+
* and `workspaces` absent or a dense array of strings, `select` absent or a function, and a `narrow`
|
|
5576
|
+
* function. The guard reads each of those members one time and never calls a method. It admits
|
|
5577
|
+
* unknown members, a class instance, and a transparent proxy, and returns false for a throwing
|
|
5578
|
+
* getter, a revoked proxy, and any other malformed input.
|
|
5579
|
+
*
|
|
5580
|
+
* @param value - The value to test
|
|
5581
|
+
* @returns True if `value` carries the members of a {@link ScopeInterface}; false otherwise
|
|
5582
|
+
*
|
|
5583
|
+
* @example
|
|
5584
|
+
* ```ts
|
|
5585
|
+
* import { createScope, isScope } from '@orkestrel/agent'
|
|
5586
|
+
*
|
|
5587
|
+
* isScope(createScope({ name: 'reader', tools: ['search'] })) // true
|
|
5588
|
+
* isScope({ id: 's1', name: 'reader' }) // false
|
|
5589
|
+
* ```
|
|
5590
|
+
*/
|
|
5591
|
+
function isScope(value) {
|
|
5592
|
+
return (0, _orkestrel_contract.holds)(() => {
|
|
5593
|
+
if (!(0, _orkestrel_contract.isObject)(value)) return false;
|
|
5594
|
+
const list = (0, _orkestrel_contract.optionalOf)((0, _orkestrel_contract.arrayOf)(_orkestrel_contract.isString));
|
|
5595
|
+
return (0, _orkestrel_contract.isString)(Reflect.get(value, "id")) && (0, _orkestrel_contract.isString)(Reflect.get(value, "name")) && list(Reflect.get(value, "instructions")) && list(Reflect.get(value, "skills")) && list(Reflect.get(value, "memos")) && list(Reflect.get(value, "tools")) && list(Reflect.get(value, "files")) && list(Reflect.get(value, "workspaces")) && (0, _orkestrel_contract.optionalOf)(_orkestrel_contract.isFunction)(Reflect.get(value, "select")) && (0, _orkestrel_contract.isFunction)(Reflect.get(value, "narrow"));
|
|
5596
|
+
});
|
|
5597
|
+
}
|
|
5153
5598
|
// src/core/contexts/memos/MemoManager.ts
|
|
5154
5599
|
/**
|
|
5155
5600
|
* Registers the revisions of a memo set — durable facts keyed by `name`, where a same-name `add`
|
|
@@ -5197,6 +5642,7 @@ var MemoManager = class {
|
|
|
5197
5642
|
* @throws {ContextError} Thrown when the `snapshot` option cannot be copied through JSON or its copy is not a memo snapshot (code `'SNAPSHOT'`); nothing is seated and nothing is emitted
|
|
5198
5643
|
*/
|
|
5199
5644
|
constructor(options) {
|
|
5645
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, MEMO_MANAGER_KEYS, "MemoManager", "MemoManagerOptions");
|
|
5200
5646
|
const supplied = options?.snapshot;
|
|
5201
5647
|
const owned = supplied === void 0 ? void 0 : copyJSON(supplied);
|
|
5202
5648
|
const snapshot = isMemoSnapshot(owned) ? owned : void 0;
|
|
@@ -5225,6 +5671,7 @@ var MemoManager = class {
|
|
|
5225
5671
|
return this.#format?.close;
|
|
5226
5672
|
}
|
|
5227
5673
|
add(input) {
|
|
5674
|
+
((0, _orkestrel_contract.isArray)(input) ? input : [input]).forEach((one) => requireRecordInput(one, memoInputContract, "MemoManager", "MemoInput"));
|
|
5228
5675
|
if ((0, _orkestrel_contract.isArray)(input)) return input.map((one) => this.#append(one));
|
|
5229
5676
|
return this.#append(input);
|
|
5230
5677
|
}
|
|
@@ -5283,9 +5730,9 @@ var MemoManager = class {
|
|
|
5283
5730
|
// src/core/contexts/scopes/Scope.ts
|
|
5284
5731
|
/**
|
|
5285
5732
|
* Represents a named, immutable filter over a richer context's items — an optional allow-list per
|
|
5286
|
-
* category (`instructions` / `skills` / `memos` / `tools` / `files`), each keyed by
|
|
5287
|
-
* identity (an instruction's, a skill's, a memo's, or a tool's `name`, a workspace
|
|
5288
|
-
* and read as an allow-list:
|
|
5733
|
+
* category (`instructions` / `skills` / `memos` / `tools` / `files` / `workspaces`), each keyed by
|
|
5734
|
+
* that category's identity (an instruction's, a skill's, a memo's, or a tool's `name`, a workspace
|
|
5735
|
+
* file's `path`, a workspace's `id`) and read as an allow-list:
|
|
5289
5736
|
* `undefined` lets everything pass, `[]` lets nothing pass, and a non-empty list passes the listed
|
|
5290
5737
|
* keys alone. `narrow` composes a tighter child by set intersection.
|
|
5291
5738
|
*
|
|
@@ -5303,6 +5750,10 @@ var MemoManager = class {
|
|
|
5303
5750
|
* `undefined` with a list yields the list, and `undefined` with `undefined` stays
|
|
5304
5751
|
* `undefined`. Narrowing can only tighten, never widen — a key excluded by a parent
|
|
5305
5752
|
* can never be re-admitted by a child. The child keeps this scope's `name` and `select` handler.
|
|
5753
|
+
* - **Exact.** The constructor refuses an own enumerable member of its input outside
|
|
5754
|
+
* `SCOPE_KEYS`, and `narrow` one of its filter outside `SCOPE_FILTER_KEYS`, with a
|
|
5755
|
+
* `ContractError` coded `'structure'`, so a misspelled list never builds an open scope. A
|
|
5756
|
+
* declared member holding `undefined` reads as omitted.
|
|
5306
5757
|
*
|
|
5307
5758
|
* @example
|
|
5308
5759
|
* ```ts
|
|
@@ -5322,8 +5773,10 @@ var Scope = class Scope {
|
|
|
5322
5773
|
memos;
|
|
5323
5774
|
tools;
|
|
5324
5775
|
files;
|
|
5776
|
+
workspaces;
|
|
5325
5777
|
select;
|
|
5326
5778
|
constructor(input) {
|
|
5779
|
+
(0, _orkestrel_contract.refuseOptionKeys)(input, SCOPE_KEYS, "Scope", "ScopeInput");
|
|
5327
5780
|
this.name = input.name;
|
|
5328
5781
|
if (input.select !== void 0) this.select = input.select;
|
|
5329
5782
|
if (input.instructions !== void 0) this.instructions = [...input.instructions];
|
|
@@ -5331,21 +5784,14 @@ var Scope = class Scope {
|
|
|
5331
5784
|
if (input.memos !== void 0) this.memos = [...input.memos];
|
|
5332
5785
|
if (input.tools !== void 0) this.tools = [...input.tools];
|
|
5333
5786
|
if (input.files !== void 0) this.files = [...input.files];
|
|
5787
|
+
if (input.workspaces !== void 0) this.workspaces = [...input.workspaces];
|
|
5334
5788
|
}
|
|
5335
5789
|
narrow(config) {
|
|
5336
|
-
|
|
5337
|
-
const skills = intersectKeys(this.skills, config.skills);
|
|
5338
|
-
const memos = intersectKeys(this.memos, config.memos);
|
|
5339
|
-
const tools = intersectKeys(this.tools, config.tools);
|
|
5340
|
-
const files = intersectKeys(this.files, config.files);
|
|
5790
|
+
(0, _orkestrel_contract.refuseOptionKeys)(config, SCOPE_FILTER_KEYS, "Scope", "ScopeFilter");
|
|
5341
5791
|
return new Scope({
|
|
5342
5792
|
name: this.name,
|
|
5343
5793
|
...this.select === void 0 ? {} : { select: this.select },
|
|
5344
|
-
...
|
|
5345
|
-
...skills === void 0 ? {} : { skills },
|
|
5346
|
-
...memos === void 0 ? {} : { memos },
|
|
5347
|
-
...tools === void 0 ? {} : { tools },
|
|
5348
|
-
...files === void 0 ? {} : { files }
|
|
5794
|
+
...intersectFilters(this, config)
|
|
5349
5795
|
});
|
|
5350
5796
|
}
|
|
5351
5797
|
};
|
|
@@ -5382,6 +5828,7 @@ var ScopeManager = class {
|
|
|
5382
5828
|
#scopes = /* @__PURE__ */ new Map();
|
|
5383
5829
|
#emitter;
|
|
5384
5830
|
constructor(options) {
|
|
5831
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, SCOPE_MANAGER_KEYS, "ScopeManager", "ScopeManagerOptions");
|
|
5385
5832
|
this.#emitter = new _orkestrel_emitter.Emitter(options);
|
|
5386
5833
|
}
|
|
5387
5834
|
get emitter() {
|
|
@@ -5455,6 +5902,7 @@ var SkillManager = class {
|
|
|
5455
5902
|
#emitter;
|
|
5456
5903
|
#format;
|
|
5457
5904
|
constructor(options) {
|
|
5905
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, SKILL_MANAGER_KEYS, "SkillManager", "SkillManagerOptions");
|
|
5458
5906
|
this.#emitter = new _orkestrel_emitter.Emitter(options);
|
|
5459
5907
|
this.#format = options?.format;
|
|
5460
5908
|
}
|
|
@@ -5471,8 +5919,12 @@ var SkillManager = class {
|
|
|
5471
5919
|
return this.#format?.close;
|
|
5472
5920
|
}
|
|
5473
5921
|
add(input) {
|
|
5474
|
-
if ((0, _orkestrel_contract.isArray)(input))
|
|
5475
|
-
|
|
5922
|
+
if ((0, _orkestrel_contract.isArray)(input)) {
|
|
5923
|
+
const skills = input.map((one) => this.#mint(one));
|
|
5924
|
+
skills.forEach((skill) => this.#store(skill));
|
|
5925
|
+
return skills;
|
|
5926
|
+
}
|
|
5927
|
+
return this.#store(this.#mint(input));
|
|
5476
5928
|
}
|
|
5477
5929
|
skill(name) {
|
|
5478
5930
|
return this.#skills.get(name);
|
|
@@ -5491,14 +5943,28 @@ var SkillManager = class {
|
|
|
5491
5943
|
this.#skills.clear();
|
|
5492
5944
|
this.#emitter.emit("clear");
|
|
5493
5945
|
}
|
|
5494
|
-
#
|
|
5495
|
-
const
|
|
5946
|
+
#mint(input) {
|
|
5947
|
+
const given = (0, _orkestrel_contract.isRecord)(input) ? input.tools : void 0;
|
|
5948
|
+
const tools = (0, _orkestrel_contract.isArray)(given) ? Array.from(given) : given;
|
|
5949
|
+
if (tools !== void 0 && !((0, _orkestrel_contract.isArray)(tools) && tools.every(_orkestrel_contract.isString))) throw new _orkestrel_contract.ContractError("SkillManager: tools must be an array of tool names", {
|
|
5950
|
+
code: "placement",
|
|
5951
|
+
context: {
|
|
5952
|
+
path: ["input", "tools"],
|
|
5953
|
+
limit: "readonly string[]",
|
|
5954
|
+
received: (0, _orkestrel_contract.preview)(given)
|
|
5955
|
+
}
|
|
5956
|
+
});
|
|
5957
|
+
requireRecordInput(input, skillInputContract, "SkillManager", "SkillInput");
|
|
5958
|
+
return {
|
|
5496
5959
|
id: crypto.randomUUID(),
|
|
5497
5960
|
name: input.name,
|
|
5498
5961
|
description: input.description,
|
|
5499
5962
|
content: input.content,
|
|
5500
|
-
files: (input.files ?? []).map((file) => (0, _orkestrel_workspace.createFile)(file))
|
|
5963
|
+
files: (input.files ?? []).map((file) => (0, _orkestrel_workspace.createFile)(file)),
|
|
5964
|
+
tools: tools === void 0 ? [] : tools
|
|
5501
5965
|
};
|
|
5966
|
+
}
|
|
5967
|
+
#store(skill) {
|
|
5502
5968
|
this.#skills.set(skill.name, skill);
|
|
5503
5969
|
this.#emitter.emit("add", skill);
|
|
5504
5970
|
return skill;
|
|
@@ -5695,8 +6161,9 @@ var DatabaseMemoStore = class {
|
|
|
5695
6161
|
* {@link ConversationManagerInterface} message source whose active conversation is `messages`, the
|
|
5696
6162
|
* {@link ToolManagerInterface} registry, and an active {@link ScopeInterface} changed through
|
|
5697
6163
|
* {@link AgentContextInterface.apply}. `build()` folds the system prompt, the scoped instructions,
|
|
5698
|
-
* and the skills
|
|
5699
|
-
* one `user` context message after it, then the conversation, and
|
|
6164
|
+
* the skills index, and the shown skills' bodies into one system message, the memos index and the
|
|
6165
|
+
* rendered workspace's text into one `user` context message after it, then the conversation, and
|
|
6166
|
+
* never reads `tools`.
|
|
5700
6167
|
*
|
|
5701
6168
|
* @remarks
|
|
5702
6169
|
* - **Composition.** `system` is the optional system prompt; `instructions` / `skills` / `memos` /
|
|
@@ -5715,40 +6182,43 @@ var DatabaseMemoStore = class {
|
|
|
5715
6182
|
* reference, no duplication), and `build()` with no selection folds that conversation's `view()`
|
|
5716
6183
|
* (its per-section summaries + live tail) as the authoritative message inclusion — the scope does
|
|
5717
6184
|
* not filter the conversation (it owns inclusion through compaction; scope filters only
|
|
5718
|
-
* instructions / skills / memos / tools / workspace files). An agent run binds a conversation at
|
|
6185
|
+
* instructions / skills / memos / tools / workspace files / workspaces). An agent run binds a conversation at
|
|
5719
6186
|
* its call, the active one unless the run names another, and builds through a selection over that
|
|
5720
6187
|
* conversation, so `conversations.switch(id)` sets the next run's default at any moment and never
|
|
5721
6188
|
* moves a live run; `select(conversation, request, signal)` checks the conversation it is given.
|
|
5722
6189
|
* - **`build()` — the scoped assembly + the format cascade.** It folds, in order,
|
|
5723
6190
|
* the system prompt, the scope-filtered instructions (a block: the section's `open` text,
|
|
5724
6191
|
* each item's rendering, then any `close` text), then the scope-filtered skills index (a block
|
|
5725
|
-
* whose entries join with a single newline)
|
|
5726
|
-
* when at least one part exists), then the scope-filtered memos
|
|
5727
|
-
*
|
|
5728
|
-
*
|
|
6192
|
+
* whose entries join with a single newline), then the shown skills' bodies, into one leading
|
|
6193
|
+
* `system` message (prepended only when at least one part exists), then the scope-filtered memos
|
|
6194
|
+
* index (the same block), the listed workspace ids, and the rendered workspace's text files into
|
|
6195
|
+
* one `user` context message (present only when one of those sections renders), then
|
|
5729
6196
|
* appends the active conversation's `view()` (the conversation owns message inclusion through
|
|
5730
6197
|
* compaction — the scope does not filter the conversation). The instruction manager resolves
|
|
5731
6198
|
* each `open` / item / `close` most-specific-first: `open` = manager-options-override >
|
|
5732
6199
|
* built-in; per item = item-override > manager-options-override > built-in; `close` =
|
|
5733
6200
|
* manager-options-override (no built-in ⇒ no closing line when unset) (see
|
|
5734
6201
|
* {@link AgentContextInterface.build}). With no override set, each section is its built-in
|
|
5735
|
-
* header + items, no closing line. The
|
|
6202
|
+
* header + items, no closing line. The rendered workspace's scoped-in image files' `base64` payload is attached to
|
|
5736
6203
|
* the last user message (a vision provider reads images off a user turn); when no user message
|
|
5737
6204
|
* exists the attachment is skipped. Built fresh each call (recomputed, never cached), so it
|
|
5738
|
-
* always reflects the current managers / messages / scope /
|
|
6205
|
+
* always reflects the current managers / messages / scope / workspaces; it never mutates a
|
|
5739
6206
|
* manager or the stored messages. `build(selection)` adds at most one `system` message of its own
|
|
5740
6207
|
* and reads the selection's messages as conversation parts without a role check; the select path
|
|
5741
6208
|
* refuses a selected `system` role as a `'ROLE'` fault over `view()`; a host's direct
|
|
5742
6209
|
* `build(selection)` is host-trusted, and a conversation a host implements itself whose `view()`
|
|
5743
6210
|
* holds a `system` message is read as host-trusted too.
|
|
5744
|
-
* - **The
|
|
5745
|
-
*
|
|
6211
|
+
* - **The rendered workspace, by carrier — the sole document/image context.**
|
|
6212
|
+
* The rendered workspace has its {@link import('@orkestrel/workspace').FileInterface} values scope-filtered by `scope.files`,
|
|
5746
6213
|
* then split: text files fold into a dedicated `## Workspace` section (fenced reference blocks)
|
|
5747
6214
|
* in the context message — a `user` message with a minted id, never stored — and image files'
|
|
5748
6215
|
* `base64` payload attaches to the last user message of the conversation, never to the context
|
|
5749
6216
|
* message. The `system` message holds host-authored text alone: the system prompt, the
|
|
5750
|
-
* instructions,
|
|
5751
|
-
*
|
|
6217
|
+
* instructions, the skills index, and last the bodies of the skills `options.disclosure` shows.
|
|
6218
|
+
* One workspace renders: the one `options.disclosure` selects, else the active one, and only
|
|
6219
|
+
* while the scope's `workspaces` list admits it; with neither, nothing renders for workspaces.
|
|
6220
|
+
* `options.switchable` adds the admitted workspace ids to the context message, after the memos
|
|
6221
|
+
* index. `build()` never moves the active workspace, and it owns this render (a `Workspace` /
|
|
5752
6222
|
* `WorkspaceManager` stays file-focused).
|
|
5753
6223
|
* - **Tools are structural, not in the prompt.** The registry is advertised to the provider
|
|
5754
6224
|
* through `tools.definitions()` (scope-filtered by the loop), never serialized into the
|
|
@@ -5775,6 +6245,7 @@ var AgentContext = class {
|
|
|
5775
6245
|
#select;
|
|
5776
6246
|
#scope;
|
|
5777
6247
|
constructor(options) {
|
|
6248
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, CONTEXT_OPTION_KEYS, "AgentContext", "AgentContextOptions");
|
|
5778
6249
|
this.#system = options?.system;
|
|
5779
6250
|
this.#instructions = options?.instructions ?? new InstructionManager();
|
|
5780
6251
|
this.#skills = options?.skills ?? new SkillManager();
|
|
@@ -5816,31 +6287,44 @@ var AgentContext = class {
|
|
|
5816
6287
|
apply(scope) {
|
|
5817
6288
|
this.#scope = scope;
|
|
5818
6289
|
}
|
|
5819
|
-
select(conversation, request, signal) {
|
|
5820
|
-
const handler = this.#scope?.select ?? this.#select;
|
|
6290
|
+
select(conversation, request, signal, scope) {
|
|
6291
|
+
const handler = scope?.select ?? this.#scope?.select ?? this.#select;
|
|
5821
6292
|
if (handler === void 0) return void 0;
|
|
5822
6293
|
return this.#check(handler, conversation, request, signal);
|
|
5823
6294
|
}
|
|
5824
|
-
build(
|
|
5825
|
-
|
|
6295
|
+
build(options) {
|
|
6296
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, BUILD_OPTION_KEYS, "AgentContext", "ContextBuildOptions");
|
|
6297
|
+
const scope = intersectFilters(this.#scope, options?.scope);
|
|
5826
6298
|
const parts = [];
|
|
5827
6299
|
if (this.#system !== void 0) parts.push(this.#system);
|
|
5828
|
-
const instructions = filterAllowList(scope
|
|
6300
|
+
const instructions = filterAllowList(scope.instructions, this.#instructions.instructions(), (one) => one.name);
|
|
5829
6301
|
const instructed = renderSection(this.#instructions.open, instructions, (one) => this.#instructions.render(one), this.#instructions.close);
|
|
5830
6302
|
if (instructed !== void 0) parts.push(instructed);
|
|
5831
|
-
const skills = filterAllowList(scope
|
|
6303
|
+
const skills = filterAllowList(scope.skills, this.#skills.skills(), (one) => one.name);
|
|
5832
6304
|
const indexed = renderSection(this.#skills.open, skills.length === 0 ? [] : [skills.map((one) => this.#skills.render(one)).join("\n")], (block) => block, this.#skills.close);
|
|
5833
6305
|
if (indexed !== void 0) parts.push(indexed);
|
|
5834
|
-
const
|
|
6306
|
+
const admitted = new Map(skills.map((one) => [one.name, one]));
|
|
6307
|
+
const bodies = renderSection(LOADED_SECTION_HEADER, [...new Set(options?.disclosure?.skills ?? [])].flatMap((name) => {
|
|
6308
|
+
const skill = admitted.get(name);
|
|
6309
|
+
return skill === void 0 || skill.content.trim() === "" ? [] : [skill];
|
|
6310
|
+
}), renderSkillBody, void 0);
|
|
6311
|
+
if (bodies !== void 0) parts.push(bodies);
|
|
6312
|
+
const workspace = resolveWorkspace(this.#workspaces, options?.disclosure?.workspace, scope.workspaces);
|
|
6313
|
+
const files = filterAllowList(scope.files, workspace?.files() ?? [], (one) => one.path);
|
|
5835
6314
|
const documented = renderSection(WORKSPACE_SECTION_HEADER, files.flatMap((file) => (0, _orkestrel_workspace.isTextContent)(file.content) ? [{
|
|
5836
6315
|
path: file.path,
|
|
5837
6316
|
language: file.content.language,
|
|
5838
6317
|
text: file.content.text
|
|
5839
6318
|
}] : []), (file) => renderFencedFile(file.path, file.language, file.text), void 0);
|
|
5840
|
-
const memos = filterAllowList(scope
|
|
6319
|
+
const memos = filterAllowList(scope.memos, this.#memos.memos(), (one) => one.name);
|
|
5841
6320
|
const remembered = renderSection(this.#memos.open, memos.length === 0 ? [] : [memos.map((one) => this.#memos.render(one)).join("\n")], (block) => block, this.#memos.close);
|
|
5842
|
-
const tail = attachUserImages(selection?.messages ?? this.#ensureConversation().view(), collectImageData(files));
|
|
5843
|
-
const
|
|
6321
|
+
const tail = attachUserImages(options?.selection?.messages ?? this.#ensureConversation().view(), collectImageData(files));
|
|
6322
|
+
const switchable = options?.switchable === true ? filterAllowList(scope.workspaces, this.#workspaces.workspaces(), (one) => one.id) : [];
|
|
6323
|
+
const slots = [
|
|
6324
|
+
remembered,
|
|
6325
|
+
renderSection(WORKSPACE_INDEX_HEADER, switchable.length === 0 ? [] : [switchable.map((one) => renderIndexLine(one.id)).join("\n")], (block) => block, void 0),
|
|
6326
|
+
documented
|
|
6327
|
+
].filter((slot) => slot !== void 0);
|
|
5844
6328
|
const context = slots.length === 0 ? [] : [{
|
|
5845
6329
|
id: crypto.randomUUID(),
|
|
5846
6330
|
role: "user",
|
|
@@ -6161,6 +6645,17 @@ function createAgentContext(options) {
|
|
|
6161
6645
|
*/
|
|
6162
6646
|
var DEFAULT_AGENT_LIMIT = 10;
|
|
6163
6647
|
/**
|
|
6648
|
+
* Opens a job tree finding that refuses a whole job, which sets it apart from a finding that names an exceeded member.
|
|
6649
|
+
*
|
|
6650
|
+
* @remarks
|
|
6651
|
+
* The `scanJobTree` helper writes it before the path of a job the contract refuses or a member read
|
|
6652
|
+
* makes unreadable, and the `AgentFanout` class reads it to open its refusal with the rule that
|
|
6653
|
+
* finding breaks. An
|
|
6654
|
+
* exceeded member's finding opens with `job`, so text later in a finding cannot pass for this
|
|
6655
|
+
* prefix.
|
|
6656
|
+
*/
|
|
6657
|
+
var JOB_REFUSAL_PREFIX = "refused ";
|
|
6658
|
+
/**
|
|
6164
6659
|
* Holds the model-facing texts the loop writes after a tool name in a denial result — the
|
|
6165
6660
|
* `denied` note for a deny with no reason, an evaluate throw, an invalid decision, or a tool
|
|
6166
6661
|
* changed after its decision; the `refused` note for an ask the approver refused; and the
|
|
@@ -6172,8 +6667,74 @@ var AUTHORITY_NOTES = Object.freeze({
|
|
|
6172
6667
|
unanswered: "it needs approval and none was given. Do not call it again; tell the user it needs approval."
|
|
6173
6668
|
});
|
|
6174
6669
|
/**
|
|
6670
|
+
* Holds the texts the built-ins return on success — `loaded`, `unloaded`, `remembered`, and
|
|
6671
|
+
* `switched` — frozen.
|
|
6672
|
+
*
|
|
6673
|
+
* @remarks
|
|
6674
|
+
* `loaded`, `unloaded`, and `switched` are the texts the conversation's stored tool messages are
|
|
6675
|
+
* matched against to derive what a run shows, so each is one fixed constant. A stored message
|
|
6676
|
+
* counts only under a call to a built-in the run enables.
|
|
6677
|
+
*/
|
|
6678
|
+
var BUILTIN_NOTES = Object.freeze({
|
|
6679
|
+
loaded: "The skill is loaded. Its instructions and tools are yours from your next step.",
|
|
6680
|
+
unloaded: "The skill is unloaded. Its instructions and tools leave from your next step.",
|
|
6681
|
+
remembered: "The memo is saved. Continue with what you were doing.",
|
|
6682
|
+
switched: "The workspace is selected. Its files are yours from your next step."
|
|
6683
|
+
});
|
|
6684
|
+
/**
|
|
6685
|
+
* Caps each built-in argument in code points, as the built-in argument shapes count them — a `name`
|
|
6686
|
+
* (a skill name, a memo name, or a workspace id), a memo `description`, and a memo `content` — and
|
|
6687
|
+
* the `candidates` one refusal lists, frozen.
|
|
6688
|
+
*/
|
|
6689
|
+
var BUILTIN_LIMITS = Object.freeze({
|
|
6690
|
+
name: 128,
|
|
6691
|
+
description: 200,
|
|
6692
|
+
content: 4e3,
|
|
6693
|
+
candidates: 20
|
|
6694
|
+
});
|
|
6695
|
+
/**
|
|
6696
|
+
* Declares each built-in tool's model-facing description and annotations, keyed by its
|
|
6697
|
+
* {@link AgentBuiltin} name, frozen.
|
|
6698
|
+
*
|
|
6699
|
+
* @remarks
|
|
6700
|
+
* `memo` is `pure` and `untrusted`, because it returns memo text a model can have written;
|
|
6701
|
+
* `remember` is `consequential`, so an `allow` verdict becomes an `ask`. A run's own tools and the
|
|
6702
|
+
* definitions `collectBuiltinDefinitions` returns both read their text and annotations here.
|
|
6703
|
+
*/
|
|
6704
|
+
var BUILTIN_TOOLS = Object.freeze({
|
|
6705
|
+
load: Object.freeze({ description: "Loads one skill from the skills index, so its instructions join yours and its tools become callable from your next step. Example: {\"skill\":\"refunds\"}. Replies with a short acknowledgement, not the instructions; call unload when you no longer need the skill." }),
|
|
6706
|
+
unload: Object.freeze({ description: "Unloads one skill you loaded, so its instructions and tools leave from your next step. Example: {\"skill\":\"refunds\"}. Replies with a short acknowledgement." }),
|
|
6707
|
+
memo: Object.freeze({
|
|
6708
|
+
description: "Reads one memo from the memos index and replies with its full text. Example: {\"name\":\"tone\"}. Reading changes nothing; call remember to write a memo.",
|
|
6709
|
+
annotations: Object.freeze({
|
|
6710
|
+
pure: true,
|
|
6711
|
+
untrusted: true
|
|
6712
|
+
})
|
|
6713
|
+
}),
|
|
6714
|
+
remember: Object.freeze({
|
|
6715
|
+
description: "Saves one memo under a name you are allowed to write: a one-line description for the memos index, and the content. Example: {\"name\":\"tone\",\"description\":\"Reply tone\",\"content\":\"Keep replies brief, because the user asked for that.\"}. Replies with a short acknowledgement; call memo to read a memo back.",
|
|
6716
|
+
annotations: Object.freeze({ consequential: true })
|
|
6717
|
+
}),
|
|
6718
|
+
switch: Object.freeze({ description: "Selects one workspace from the workspaces index, so its files join your context from your next step. Example: {\"workspace\":\"orders\"}. Replies with a short acknowledgement." })
|
|
6719
|
+
});
|
|
6720
|
+
/**
|
|
6721
|
+
* Matches one control, format, line-separator, or paragraph-separator code point — the class
|
|
6722
|
+
* `renderArguments` encodes and a memo's one-line name and description may not hold, frozen.
|
|
6723
|
+
*
|
|
6724
|
+
* @remarks
|
|
6725
|
+
* Not global, so a `test` call keeps no state between calls; a global replace builds its own
|
|
6726
|
+
* global copy from `source`.
|
|
6727
|
+
*/
|
|
6728
|
+
var ARGUMENT_CONTROL_PATTERN = Object.freeze(/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u);
|
|
6729
|
+
/**
|
|
6730
|
+
* Matches a text that is blank: empty after white space and default-ignorable code points, such
|
|
6731
|
+
* as U+3164, are removed, frozen.
|
|
6732
|
+
*/
|
|
6733
|
+
var ARGUMENT_BLANK_PATTERN = Object.freeze(/^[\p{White_Space}\p{Default_Ignorable_Code_Point}]*$/u);
|
|
6734
|
+
/**
|
|
6175
6735
|
* Holds the model-facing text the loop writes after a tool name in the denial result of a call
|
|
6176
|
-
* outside the
|
|
6736
|
+
* outside the turn's tool ceiling, the context scope's tool allow-list intersected with the run
|
|
6737
|
+
* scope's, or whose name no definition advertises this turn.
|
|
6177
6738
|
*/
|
|
6178
6739
|
var SCOPE_NOTE = "it is not one of the listed tools. Use a listed tool or answer with what you have.";
|
|
6179
6740
|
/**
|
|
@@ -6192,6 +6753,92 @@ var MESSAGE_TOKEN_OVERHEAD = 4;
|
|
|
6192
6753
|
* exact count.
|
|
6193
6754
|
*/
|
|
6194
6755
|
var IMAGE_TOKEN_ESTIMATE = 512;
|
|
6756
|
+
/**
|
|
6757
|
+
* Lists the members `AgentOptions` declares, frozen — the keys the `Agent` constructor admits.
|
|
6758
|
+
*
|
|
6759
|
+
* @remarks
|
|
6760
|
+
* The constructor passes the list to `refuseOptionKeys` of `@orkestrel/contract`, so an own
|
|
6761
|
+
* enumerable member outside it refuses with a `ContractError` coded `'structure'`.
|
|
6762
|
+
*/
|
|
6763
|
+
var AGENT_OPTION_KEYS = Object.freeze([
|
|
6764
|
+
"on",
|
|
6765
|
+
"error",
|
|
6766
|
+
"system",
|
|
6767
|
+
"tools",
|
|
6768
|
+
"instructions",
|
|
6769
|
+
"skills",
|
|
6770
|
+
"memos",
|
|
6771
|
+
"workspaces",
|
|
6772
|
+
"scope",
|
|
6773
|
+
"limit",
|
|
6774
|
+
"timeout",
|
|
6775
|
+
"budget",
|
|
6776
|
+
"allowance",
|
|
6777
|
+
"scheduler",
|
|
6778
|
+
"signal",
|
|
6779
|
+
"authority",
|
|
6780
|
+
"conversations",
|
|
6781
|
+
"window",
|
|
6782
|
+
"strict",
|
|
6783
|
+
"select",
|
|
6784
|
+
"builtins",
|
|
6785
|
+
"workspace"
|
|
6786
|
+
]);
|
|
6787
|
+
/**
|
|
6788
|
+
* Lists the members `AgentRunOptions` declares, frozen — the keys the `stream` and `generate`
|
|
6789
|
+
* methods admit.
|
|
6790
|
+
*
|
|
6791
|
+
* @remarks
|
|
6792
|
+
* The `stream` method passes the list to `refuseOptionKeys` of `@orkestrel/contract`, so an own
|
|
6793
|
+
* enumerable member outside it refuses with a `ContractError` coded `'structure'`.
|
|
6794
|
+
*/
|
|
6795
|
+
var AGENT_RUN_KEYS = Object.freeze([
|
|
6796
|
+
"think",
|
|
6797
|
+
"schema",
|
|
6798
|
+
"limit",
|
|
6799
|
+
"timeout",
|
|
6800
|
+
"budget",
|
|
6801
|
+
"allowance",
|
|
6802
|
+
"signal",
|
|
6803
|
+
"conversation",
|
|
6804
|
+
"prompt",
|
|
6805
|
+
"scope",
|
|
6806
|
+
"workspace"
|
|
6807
|
+
]);
|
|
6808
|
+
/** Lists the members `AuthorityOptions` declares, frozen — the keys the `Authority` constructor admits. */
|
|
6809
|
+
var AUTHORITY_OPTION_KEYS = Object.freeze([
|
|
6810
|
+
"rules",
|
|
6811
|
+
"fallback",
|
|
6812
|
+
"ask"
|
|
6813
|
+
]);
|
|
6814
|
+
/** Lists the members `AgentRegistryOptions` declares, frozen — the keys the `AgentRegistry` constructor admits. */
|
|
6815
|
+
var REGISTRY_OPTION_KEYS = Object.freeze([
|
|
6816
|
+
"providers",
|
|
6817
|
+
"tools",
|
|
6818
|
+
"authorities",
|
|
6819
|
+
"schedulers",
|
|
6820
|
+
"store",
|
|
6821
|
+
"skills",
|
|
6822
|
+
"memos",
|
|
6823
|
+
"workspaces"
|
|
6824
|
+
]);
|
|
6825
|
+
/** Lists the members `AgentQueueOptions` declares, frozen — the keys `createAgentQueue` admits. */
|
|
6826
|
+
var QUEUE_OPTION_KEYS = Object.freeze([
|
|
6827
|
+
"registry",
|
|
6828
|
+
"partial",
|
|
6829
|
+
"concurrency",
|
|
6830
|
+
"retries",
|
|
6831
|
+
"timeout",
|
|
6832
|
+
"store"
|
|
6833
|
+
]);
|
|
6834
|
+
/** Lists the members `AgentRunnerOptions` declares, frozen — the keys `createAgentRunner` admits. */
|
|
6835
|
+
var RUNNER_OPTION_KEYS = Object.freeze([
|
|
6836
|
+
"registry",
|
|
6837
|
+
"partial",
|
|
6838
|
+
"concurrency",
|
|
6839
|
+
"retries",
|
|
6840
|
+
"timeout"
|
|
6841
|
+
]);
|
|
6195
6842
|
// src/core/agents/errors.ts
|
|
6196
6843
|
/**
|
|
6197
6844
|
* Reports an {@link AgentInterface} run that ended {@link AgentResult.partial} under a
|
|
@@ -6275,6 +6922,9 @@ function isAgentJobError(value) {
|
|
|
6275
6922
|
* `'AUTHORITY'` reports an `Authority` built with no valid `fallback`, a rule with no valid `verdict`
|
|
6276
6923
|
* (its `context` names the `rule` index), and an agent built with `tools` and no `authority`. Each
|
|
6277
6924
|
* throws at construction, with the options a host passes to correct it in the message.
|
|
6925
|
+
* `'BUILTIN'` reports a `builtins` entry that is not a built-in or repeats one, `unload` enabled
|
|
6926
|
+
* without `load`, and a tool manager holding a tool under an enabled built-in's name, each thrown
|
|
6927
|
+
* at construction.
|
|
6278
6928
|
* `'CONCURRENCY'` reports a run started while another run on the same agent is live, when the
|
|
6279
6929
|
* agent carries a `window` (a shared context budget) or when the run's effective `budget` or
|
|
6280
6930
|
* `allowance` is an instance a live run holds — the second run's `start()` would replace that
|
|
@@ -6283,18 +6933,18 @@ function isAgentJobError(value) {
|
|
|
6283
6933
|
* `agent` id, and the `conversation` id for a shared conversation. For concurrent runs, use
|
|
6284
6934
|
* separate agents, or one agent with no `window`, distinct budget and allowance instances per
|
|
6285
6935
|
* run, and a distinct conversation per run.
|
|
6286
|
-
* `'LIMIT'` reports a construction or per-run `limit` that is not a positive safe integer
|
|
6287
|
-
*
|
|
6288
|
-
*
|
|
6289
|
-
*
|
|
6290
|
-
* `
|
|
6936
|
+
* `'LIMIT'` reports a construction or per-run `limit` that is not a positive safe integer.
|
|
6937
|
+
* `'REGISTRY'` reports a rehydration name absent from its registry pool, on `provider` / `tool` /
|
|
6938
|
+
* `authority` / `scheduler` / `build`, whose `context` names the `category` and the `name`, and a
|
|
6939
|
+
* job `build` refuses as a record, whose `context` holds the contract's `faults` or the
|
|
6940
|
+
* `refusals` lines `collectJobRefusals` returns. `'WINDOW'` reports a `strict` run whose
|
|
6291
6941
|
* complete provider request is still over the `window` budget, equal to its `max` included, after
|
|
6292
6942
|
* the one permitted fold or with no fold possible; it settles the run `error` before the provider
|
|
6293
6943
|
* is called, and its `context` holds the `agent` id, the measured `consumed`, and the window `max`.
|
|
6294
6944
|
* Narrow a caught value with {@link isAgentError} and branch on `error.code`.
|
|
6295
6945
|
*/
|
|
6296
6946
|
var AgentError = class extends Error {
|
|
6297
|
-
/** Names the machine-readable condition — `'APPROVER'`: an `ask` on an authority that has no approver, or one its approver refuses; `'AUTHORITY'`: an authority missing its fallback or a rule's verdict, or tools set with no authority; `'CONCURRENCY'`: a run sharing accounting or a conversation with a live run; `'LIMIT'`: a `limit` that is not a positive safe integer; `'REGISTRY'`: a rehydration name absent from its registry pool; `'WINDOW'`: a strict run whose complete request is still over its window budget. */
|
|
6947
|
+
/** Names the machine-readable condition — `'APPROVER'`: an `ask` on an authority that has no approver, or one its approver refuses; `'AUTHORITY'`: an authority missing its fallback or a rule's verdict, or tools set with no authority; `'BUILTIN'`: a built-in list the agent cannot serve; `'CONCURRENCY'`: a run sharing accounting or a conversation with a live run; `'LIMIT'`: a `limit` that is not a positive safe integer; `'REGISTRY'`: a rehydration name absent from its registry pool; `'WINDOW'`: a strict run whose complete request is still over its window budget. */
|
|
6298
6948
|
code;
|
|
6299
6949
|
/** Holds the values that locate the failure, such as the `agent` id of a refused run, or `undefined` when none were supplied. */
|
|
6300
6950
|
context;
|
|
@@ -6333,7 +6983,7 @@ function isAgentError(value) {
|
|
|
6333
6983
|
return (0, _orkestrel_contract.holds)(() => {
|
|
6334
6984
|
if (!(value instanceof Error && _orkestrel_contract.INTRINSICS.prototype(value) !== Error.prototype && _orkestrel_contract.INTRINSICS.describe(value, Symbol.for("@orkestrel/agent.AgentError"))?.value === value)) return false;
|
|
6335
6985
|
const code = _orkestrel_contract.INTRINSICS.reflect.read(value, "code");
|
|
6336
|
-
if (!(code === "APPROVER" || code === "AUTHORITY" || code === "CONCURRENCY" || code === "LIMIT" || code === "REGISTRY" || code === "SCHEMA" || code === "WINDOW")) return false;
|
|
6986
|
+
if (!(code === "APPROVER" || code === "AUTHORITY" || code === "BUILTIN" || code === "CONCURRENCY" || code === "LIMIT" || code === "REGISTRY" || code === "SCHEMA" || code === "WINDOW")) return false;
|
|
6337
6987
|
const context = _orkestrel_contract.INTRINSICS.reflect.read(value, "context");
|
|
6338
6988
|
return context === void 0 || (0, _orkestrel_contract.isObject)(context) || (0, _orkestrel_contract.isFunction)(context);
|
|
6339
6989
|
});
|
|
@@ -6344,20 +6994,45 @@ function isAgentError(value) {
|
|
|
6344
6994
|
* refuses instead of running an agent without it.
|
|
6345
6995
|
*
|
|
6346
6996
|
* @remarks
|
|
6347
|
-
* `children` holds JSON records, because each child crosses this shape at its own `build`.
|
|
6348
|
-
*
|
|
6349
|
-
*
|
|
6997
|
+
* `children` holds JSON records, because each child crosses this shape at its own `build`. Each
|
|
6998
|
+
* bound states what the build reads: `limit` an integer from 1 through `Number.MAX_SAFE_INTEGER`,
|
|
6999
|
+
* `timeout` an integer from 0 through `MAX_TIMEOUT_MS`, and `budget` a number of at least 0. A seed
|
|
7000
|
+
* message plays a `ConversationRole`, so a `system` message refuses here. A `scope` holds the
|
|
7001
|
+
* `skills`, `memos`, and `workspaces` lists, each required, and no other member; `builtins` admits
|
|
7002
|
+
* only the names of `AgentBuiltin`, and `collectJobRefusals` reads what a list of them cannot state.
|
|
6350
7003
|
*/
|
|
6351
7004
|
var agentJobShape = (0, _orkestrel_contract.objectShape)({
|
|
6352
7005
|
provider: (0, _orkestrel_contract.stringShape)(),
|
|
6353
|
-
messages: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)(
|
|
7006
|
+
messages: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)((0, _orkestrel_contract.objectShape)({
|
|
7007
|
+
...messageInputShape.properties,
|
|
7008
|
+
role: (0, _orkestrel_contract.literalShape)(CONVERSATION_ROLES)
|
|
7009
|
+
}))),
|
|
6354
7010
|
system: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)()),
|
|
6355
7011
|
tools: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)((0, _orkestrel_contract.stringShape)())),
|
|
6356
7012
|
authority: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)()),
|
|
6357
7013
|
scheduler: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)()),
|
|
6358
|
-
limit: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.
|
|
6359
|
-
|
|
6360
|
-
|
|
7014
|
+
limit: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.integerShape)({
|
|
7015
|
+
min: 1,
|
|
7016
|
+
max: Number.MAX_SAFE_INTEGER
|
|
7017
|
+
})),
|
|
7018
|
+
timeout: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.integerShape)({
|
|
7019
|
+
min: 0,
|
|
7020
|
+
max: _orkestrel_timeout.MAX_TIMEOUT_MS
|
|
7021
|
+
})),
|
|
7022
|
+
budget: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.numberShape)({ min: 0 })),
|
|
7023
|
+
scope: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.objectShape)({
|
|
7024
|
+
skills: (0, _orkestrel_contract.arrayShape)((0, _orkestrel_contract.stringShape)()),
|
|
7025
|
+
memos: (0, _orkestrel_contract.arrayShape)((0, _orkestrel_contract.stringShape)()),
|
|
7026
|
+
workspaces: (0, _orkestrel_contract.arrayShape)((0, _orkestrel_contract.stringShape)())
|
|
7027
|
+
})),
|
|
7028
|
+
workspace: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)()),
|
|
7029
|
+
builtins: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)((0, _orkestrel_contract.literalShape)([
|
|
7030
|
+
"load",
|
|
7031
|
+
"unload",
|
|
7032
|
+
"memo",
|
|
7033
|
+
"remember",
|
|
7034
|
+
"switch"
|
|
7035
|
+
]))),
|
|
6361
7036
|
children: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)((0, _orkestrel_contract.recordShape)((0, _orkestrel_contract.jsonShape)())))
|
|
6362
7037
|
});
|
|
6363
7038
|
/**
|
|
@@ -6427,6 +7102,74 @@ var agentAnswerShape = (0, _orkestrel_contract.objectShape)({
|
|
|
6427
7102
|
id: (0, _orkestrel_contract.stringShape)(),
|
|
6428
7103
|
approved: (0, _orkestrel_contract.booleanShape)()
|
|
6429
7104
|
});
|
|
7105
|
+
/**
|
|
7106
|
+
* Describes the arguments of the `load` and `unload` built-ins — one `skill` name, non-empty and
|
|
7107
|
+
* at most `BUILTIN_LIMITS.name` code points.
|
|
7108
|
+
*
|
|
7109
|
+
* @remarks
|
|
7110
|
+
* The tool's door refuses a missing, empty, over-long, or extra member before the handler runs,
|
|
7111
|
+
* and repairs a value that converts to a string without loss, such as a number. A value of white
|
|
7112
|
+
* space alone passes the door; the handler refuses it as it refuses any name it cannot use.
|
|
7113
|
+
*/
|
|
7114
|
+
var loadToolShape = (0, _orkestrel_contract.objectShape)({ skill: (0, _orkestrel_contract.stringShape)({
|
|
7115
|
+
min: 1,
|
|
7116
|
+
max: BUILTIN_LIMITS.name,
|
|
7117
|
+
description: "A skill name from the skills index"
|
|
7118
|
+
}) });
|
|
7119
|
+
/**
|
|
7120
|
+
* Describes the arguments of the `memo` built-in — one memo `name`, non-empty and at most
|
|
7121
|
+
* `BUILTIN_LIMITS.name` code points.
|
|
7122
|
+
*
|
|
7123
|
+
* @remarks
|
|
7124
|
+
* The tool's door refuses a missing, empty, over-long, or extra member before the handler runs,
|
|
7125
|
+
* and repairs a value that converts to a string without loss, such as a number. A value of white
|
|
7126
|
+
* space alone passes the door; the handler refuses it as it refuses any name it cannot use.
|
|
7127
|
+
*/
|
|
7128
|
+
var memoToolShape = (0, _orkestrel_contract.objectShape)({ name: (0, _orkestrel_contract.stringShape)({
|
|
7129
|
+
min: 1,
|
|
7130
|
+
max: BUILTIN_LIMITS.name,
|
|
7131
|
+
description: "A memo name from the memos index"
|
|
7132
|
+
}) });
|
|
7133
|
+
/**
|
|
7134
|
+
* Describes the arguments of the `remember` built-in — a memo `name`, the one-line `description`
|
|
7135
|
+
* the memos index shows, and the `content`, each non-empty and within its `BUILTIN_LIMITS` cap.
|
|
7136
|
+
*
|
|
7137
|
+
* @remarks
|
|
7138
|
+
* The tool's door refuses a missing, empty, over-long, or extra member before the handler runs,
|
|
7139
|
+
* and repairs a value that converts to a string without loss, such as a number. A value of white
|
|
7140
|
+
* space alone passes the door; the handler refuses it as it refuses any name it cannot use.
|
|
7141
|
+
*/
|
|
7142
|
+
var rememberToolShape = (0, _orkestrel_contract.objectShape)({
|
|
7143
|
+
name: (0, _orkestrel_contract.stringShape)({
|
|
7144
|
+
min: 1,
|
|
7145
|
+
max: BUILTIN_LIMITS.name,
|
|
7146
|
+
description: "A memo name the host lists for you to write"
|
|
7147
|
+
}),
|
|
7148
|
+
description: (0, _orkestrel_contract.stringShape)({
|
|
7149
|
+
min: 1,
|
|
7150
|
+
max: BUILTIN_LIMITS.description,
|
|
7151
|
+
description: "One line the memos index shows"
|
|
7152
|
+
}),
|
|
7153
|
+
content: (0, _orkestrel_contract.stringShape)({
|
|
7154
|
+
min: 1,
|
|
7155
|
+
max: BUILTIN_LIMITS.content,
|
|
7156
|
+
description: "The fact, why it holds, and how to apply it"
|
|
7157
|
+
})
|
|
7158
|
+
});
|
|
7159
|
+
/**
|
|
7160
|
+
* Describes the arguments of the `switch` built-in — one `workspace` id, non-empty and at most
|
|
7161
|
+
* `BUILTIN_LIMITS.name` code points.
|
|
7162
|
+
*
|
|
7163
|
+
* @remarks
|
|
7164
|
+
* The tool's door refuses a missing, empty, over-long, or extra member before the handler runs,
|
|
7165
|
+
* and repairs a value that converts to a string without loss, such as a number. A value of white
|
|
7166
|
+
* space alone passes the door; the handler refuses it as it refuses any name it cannot use.
|
|
7167
|
+
*/
|
|
7168
|
+
var switchToolShape = (0, _orkestrel_contract.objectShape)({ workspace: (0, _orkestrel_contract.stringShape)({
|
|
7169
|
+
min: 1,
|
|
7170
|
+
max: BUILTIN_LIMITS.name,
|
|
7171
|
+
description: "A workspace id from the workspaces index"
|
|
7172
|
+
}) });
|
|
6430
7173
|
// src/core/agents/contracts.ts
|
|
6431
7174
|
/** Validates and projects agent frames at a JSON wire boundary. */
|
|
6432
7175
|
var agentFrameContract = (0, _orkestrel_contract.createContract)(agentFrameShape);
|
|
@@ -6436,131 +7179,64 @@ var agentPromptContract = (0, _orkestrel_contract.createContract)(agentPromptSha
|
|
|
6436
7179
|
var agentJobContract = (0, _orkestrel_contract.createContract)(agentJobShape);
|
|
6437
7180
|
/** Validates and projects a page's answers to `ask` frames at a JSON wire boundary. */
|
|
6438
7181
|
var agentAnswerContract = (0, _orkestrel_contract.createContract)(agentAnswerShape);
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
}
|
|
6483
|
-
};
|
|
6484
|
-
// src/core/agents/Channel.ts
|
|
7182
|
+
/** Checks the arguments of the `load` and `unload` built-ins; its schema is the parameters they advertise. */
|
|
7183
|
+
var loadToolContract = (0, _orkestrel_contract.createContract)(loadToolShape);
|
|
7184
|
+
/** Checks the arguments of the `memo` built-in; its schema is the parameters it advertises. */
|
|
7185
|
+
var memoToolContract = (0, _orkestrel_contract.createContract)(memoToolShape);
|
|
7186
|
+
/** Checks the arguments of the `remember` built-in; its schema is the parameters it advertises. */
|
|
7187
|
+
var rememberToolContract = (0, _orkestrel_contract.createContract)(rememberToolShape);
|
|
7188
|
+
/** Checks the arguments of the `switch` built-in; its schema is the parameters it advertises. */
|
|
7189
|
+
var switchToolContract = (0, _orkestrel_contract.createContract)(switchToolShape);
|
|
7190
|
+
/**
|
|
7191
|
+
* Pairs each built-in's argument shape, which its tool's door compiles, with its compiled contract,
|
|
7192
|
+
* keyed by its {@link AgentBuiltin} name, frozen.
|
|
7193
|
+
*
|
|
7194
|
+
* @remarks
|
|
7195
|
+
* The one map from a built-in to its arguments: `createBuiltinTool` builds the door from `shape`,
|
|
7196
|
+
* and `collectBuiltinDefinitions` and `collectDisclosure` read `contract`, so the advertised
|
|
7197
|
+
* parameters, the door, and the derivation admit by one rule. The record declares no shape: each
|
|
7198
|
+
* `shape` is a reference to the one `shapers.ts` declares, held beside the contract compiled from
|
|
7199
|
+
* it, because the tool door takes a shape and the derivation takes a compiled contract. The record
|
|
7200
|
+
* is data, as the rest of this file is.
|
|
7201
|
+
*/
|
|
7202
|
+
var builtinToolContracts = Object.freeze({
|
|
7203
|
+
load: Object.freeze({
|
|
7204
|
+
shape: loadToolShape,
|
|
7205
|
+
contract: loadToolContract
|
|
7206
|
+
}),
|
|
7207
|
+
unload: Object.freeze({
|
|
7208
|
+
shape: loadToolShape,
|
|
7209
|
+
contract: loadToolContract
|
|
7210
|
+
}),
|
|
7211
|
+
memo: Object.freeze({
|
|
7212
|
+
shape: memoToolShape,
|
|
7213
|
+
contract: memoToolContract
|
|
7214
|
+
}),
|
|
7215
|
+
remember: Object.freeze({
|
|
7216
|
+
shape: rememberToolShape,
|
|
7217
|
+
contract: rememberToolContract
|
|
7218
|
+
}),
|
|
7219
|
+
switch: Object.freeze({
|
|
7220
|
+
shape: switchToolShape,
|
|
7221
|
+
contract: switchToolContract
|
|
7222
|
+
})
|
|
7223
|
+
});
|
|
7224
|
+
// src/core/agents/validators.ts
|
|
6485
7225
|
/**
|
|
6486
|
-
*
|
|
6487
|
-
* and ends it (`close` / `fail`) regardless of consumption, while a consumer reads them back live
|
|
6488
|
-
* through the `drain` async-iterator. Decoupling write from read is what lets a producer make
|
|
6489
|
-
* progress without a consumer pulling, and it is why an agent's `result` settles whether or not
|
|
6490
|
-
* its `events` are drained.
|
|
7226
|
+
* Checks whether a value names a built-in: `load`, `unload`, `memo`, `remember`, or `switch`.
|
|
6491
7227
|
*
|
|
6492
|
-
* @
|
|
6493
|
-
*
|
|
6494
|
-
* cell after the `#tail` cell, and the `close` and `fail` methods flag the end; each then fires and
|
|
6495
|
-
* clears every parked resolver, so each parked reader wakes, reads the `#head` cell, and either
|
|
6496
|
-
* takes the next chunk, returns (on `close`), throws (on `fail`), or parks again when another
|
|
6497
|
-
* reader took the chunk first. Taking a chunk reads its value and moves `#head` to the next cell
|
|
6498
|
-
* synchronously, so each value reaches exactly one reader, a push and a take each cost constant
|
|
6499
|
-
* time, and every taken cell but the newest is released at once; the `#tail` field holds the newest
|
|
6500
|
-
* cell until the next push links after it, so a channel retains at most one taken value. A reader
|
|
6501
|
-
* suspended at its `yield` expression holds only the value it took, never a cell, so the bound on
|
|
6502
|
-
* retained values covers both the channel's fields and a suspended reader.
|
|
6503
|
-
* A `return()` issued while a read is parked waits for the next `push`, `close`, or `fail`.
|
|
6504
|
-
* The parked read takes a pushed value before that `return()` settles.
|
|
7228
|
+
* @param value - The unknown name candidate
|
|
7229
|
+
* @returns True if the value names a built-in; false otherwise
|
|
6505
7230
|
*
|
|
6506
7231
|
* @example
|
|
6507
7232
|
* ```ts
|
|
6508
|
-
*
|
|
6509
|
-
*
|
|
6510
|
-
* const channel = new Channel<number>()
|
|
6511
|
-
* channel.push(1)
|
|
6512
|
-
* channel.close()
|
|
6513
|
-
* for await (const value of channel.drain()) {
|
|
6514
|
-
* value // 1
|
|
6515
|
-
* }
|
|
7233
|
+
* isAgentBuiltin('load') // true
|
|
7234
|
+
* isAgentBuiltin('open') // false
|
|
6516
7235
|
* ```
|
|
6517
7236
|
*/
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
#waiting = [];
|
|
6522
|
-
#closed = false;
|
|
6523
|
-
#failure;
|
|
6524
|
-
push(value) {
|
|
6525
|
-
const cell = new ChannelCell(value);
|
|
6526
|
-
this.#tail?.link(cell);
|
|
6527
|
-
this.#tail = cell;
|
|
6528
|
-
this.#head ??= cell;
|
|
6529
|
-
this.#signal();
|
|
6530
|
-
}
|
|
6531
|
-
close() {
|
|
6532
|
-
this.#closed = true;
|
|
6533
|
-
this.#signal();
|
|
6534
|
-
}
|
|
6535
|
-
fail(error) {
|
|
6536
|
-
if (this.#failure === void 0) this.#failure = { error };
|
|
6537
|
-
this.#closed = true;
|
|
6538
|
-
this.#signal();
|
|
6539
|
-
}
|
|
6540
|
-
async *drain() {
|
|
6541
|
-
for (;;) {
|
|
6542
|
-
while (this.#head !== void 0) {
|
|
6543
|
-
const value = this.#head.value;
|
|
6544
|
-
this.#head = this.#head.next;
|
|
6545
|
-
yield value;
|
|
6546
|
-
}
|
|
6547
|
-
if (this.#failure !== void 0) throw this.#failure.error;
|
|
6548
|
-
if (this.#closed) return;
|
|
6549
|
-
await this.#park();
|
|
6550
|
-
}
|
|
6551
|
-
}
|
|
6552
|
-
#signal() {
|
|
6553
|
-
const waiting = this.#waiting;
|
|
6554
|
-
this.#waiting = [];
|
|
6555
|
-
for (const wake of waiting) wake();
|
|
6556
|
-
}
|
|
6557
|
-
#park() {
|
|
6558
|
-
return new Promise((resolve) => {
|
|
6559
|
-
this.#waiting = [...this.#waiting, resolve];
|
|
6560
|
-
});
|
|
6561
|
-
}
|
|
6562
|
-
};
|
|
6563
|
-
// src/core/agents/validators.ts
|
|
7237
|
+
function isAgentBuiltin(value) {
|
|
7238
|
+
return value === "load" || value === "unload" || value === "memo" || value === "remember" || value === "switch";
|
|
7239
|
+
}
|
|
6564
7240
|
/**
|
|
6565
7241
|
* Checks whether a value is an authority verdict: `allow`, `ask`, or `deny`.
|
|
6566
7242
|
*
|
|
@@ -6601,6 +7277,35 @@ function isAuthorityDecision(value) {
|
|
|
6601
7277
|
reason: _orkestrel_contract.isString
|
|
6602
7278
|
}, ["reason"])(value);
|
|
6603
7279
|
}
|
|
7280
|
+
/**
|
|
7281
|
+
* Checks whether a value is an agent caller: a plain record whose own keys are exactly `workspace`
|
|
7282
|
+
* and `files`, whose `workspace` is `undefined` or an object, and whose `files` is `undefined` or an
|
|
7283
|
+
* array of strings.
|
|
7284
|
+
*
|
|
7285
|
+
* @remarks
|
|
7286
|
+
* Total: each member is read once, and an omitted, inherited, or undeclared member, a throwing
|
|
7287
|
+
* accessor, and a revoked `Proxy` return false rather than throw. It checks the shape a caller
|
|
7288
|
+
* carries, never that `workspace` is a live workspace: a value that crossed a wire passes when it
|
|
7289
|
+
* holds that shape, so accept its `workspace` only as the very instance your own registry holds
|
|
7290
|
+
* under that id.
|
|
7291
|
+
*
|
|
7292
|
+
* @param value - The unknown caller candidate, such as `ToolContext.caller`
|
|
7293
|
+
* @returns True if the value satisfies the caller contract; false otherwise
|
|
7294
|
+
*
|
|
7295
|
+
* @example
|
|
7296
|
+
* ```ts
|
|
7297
|
+
* isAgentCaller({ workspace: undefined, files: undefined }) // true
|
|
7298
|
+
* isAgentCaller({ workspace: 'returns' }) // false
|
|
7299
|
+
* ```
|
|
7300
|
+
*/
|
|
7301
|
+
function isAgentCaller(value) {
|
|
7302
|
+
return (0, _orkestrel_contract.holds)(() => {
|
|
7303
|
+
if (!(0, _orkestrel_contract.isRecord)(value) || !(0, _orkestrel_contract.matchesExactKeys)(value, ["workspace", "files"])) return false;
|
|
7304
|
+
const workspace = value.workspace;
|
|
7305
|
+
const files = value.files;
|
|
7306
|
+
return (workspace === void 0 || (0, _orkestrel_contract.isObject)(workspace)) && (files === void 0 || (0, _orkestrel_contract.arrayOf)(_orkestrel_contract.isString)(files));
|
|
7307
|
+
});
|
|
7308
|
+
}
|
|
6604
7309
|
// src/core/agents/helpers.ts
|
|
6605
7310
|
/**
|
|
6606
7311
|
* Projects an unknown value onto a fresh, exact `JSONValue` representation of an
|
|
@@ -7081,6 +7786,118 @@ function handleAgentQueueJob(registry, partial, input, context) {
|
|
|
7081
7786
|
return settleAgentJob(registry.build(input, context.signal), partial);
|
|
7082
7787
|
}
|
|
7083
7788
|
/**
|
|
7789
|
+
* Collects the refusals a job the contract admitted would meet at its build: tools or built-ins with
|
|
7790
|
+
* no authority, a `builtins` list `requireBuiltins` refuses, and a built-in its own `tools` name.
|
|
7791
|
+
*
|
|
7792
|
+
* @remarks
|
|
7793
|
+
* Each line names the member first, then the refusal and its remedy. The `builtins` line of a list
|
|
7794
|
+
* `requireBuiltins` refuses carries that error's message; a job's tool manager holds exactly its
|
|
7795
|
+
* `tools`, so a built-in its `tools` list also names is the name a manager would already hold.
|
|
7796
|
+
* `AgentRegistry.build` refuses a job with any line, and `scanJobTree` refuses each such job of a
|
|
7797
|
+
* tree before anything spawns.
|
|
7798
|
+
*
|
|
7799
|
+
* @param job - The `tools`, `builtins`, and `authority` of a job `agentJobContract` admitted
|
|
7800
|
+
* @returns One line a refusal, in member order; empty when the job builds
|
|
7801
|
+
*
|
|
7802
|
+
* @example
|
|
7803
|
+
* ```ts
|
|
7804
|
+
* collectJobRefusals({ provider: 'main', tools: ['search'] })
|
|
7805
|
+
* // ["authority: a job that names tools or builtins and no authority cannot run them; add authority: 'NAME' to the job and register that NAME under authorities"]
|
|
7806
|
+
* ```
|
|
7807
|
+
*/
|
|
7808
|
+
function collectJobRefusals(job) {
|
|
7809
|
+
const tools = job.tools ?? [];
|
|
7810
|
+
const builtins = job.builtins ?? [];
|
|
7811
|
+
const checked = (0, _orkestrel_contract.attempt)(() => requireBuiltins(job.builtins, { tool: () => void 0 }));
|
|
7812
|
+
const shared = builtins.filter((name) => tools.includes(name));
|
|
7813
|
+
return [
|
|
7814
|
+
(tools.length > 0 || builtins.length > 0) && job.authority === void 0 ? "authority: a job that names tools or builtins and no authority cannot run them; add authority: 'NAME' to the job and register that NAME under authorities" : void 0,
|
|
7815
|
+
checked.success ? void 0 : `builtins: ${(0, _orkestrel_contract.errorToMessage)(checked.error)}`,
|
|
7816
|
+
shared.length === 0 ? void 0 : `builtins: names ${shared.map((name) => (0, _orkestrel_contract.preview)(name)).join(", ")}, which its tools also name; rename that tool or leave the name out of builtins`
|
|
7817
|
+
].filter((line) => line !== void 0);
|
|
7818
|
+
}
|
|
7819
|
+
/**
|
|
7820
|
+
* Scans every job under a root for a member that exceeds its own parent's; an empty result clears the tree.
|
|
7821
|
+
*
|
|
7822
|
+
* @remarks
|
|
7823
|
+
* The walk audits each job, the root included, through `agentJobContract` before it reads a member,
|
|
7824
|
+
* because a job's `children` cross that contract as JSON records, and then reads each job the
|
|
7825
|
+
* contract admits through `collectJobRefusals`. A job the contract refuses yields one finding with its
|
|
7826
|
+
* faults, or with an unreadable note when a member read throws; a job its build would refuse yields
|
|
7827
|
+
* one finding a refusal, its path, a dot, and the line; and the walk reads nothing under either. A
|
|
7828
|
+
* child is within its parent when each of
|
|
7829
|
+
* its `tools` is one its parent names (a parent with no `tools` names none), each of its `builtins`
|
|
7830
|
+
* is one its parent names (a parent with no `builtins` names none), each name in each list of its
|
|
7831
|
+
* `scope` is in its parent's same list (a job with no `scope` holds the three lists empty), its
|
|
7832
|
+
* `authority` and `scheduler` are absent or its parent's, its `provider` is its parent's, its `limit`
|
|
7833
|
+
* is at most its parent's (an absent `limit` reads `DEFAULT_AGENT_LIMIT`), and, where its parent
|
|
7834
|
+
* holds a `timeout` or a `budget`, it holds one no larger. `system`, `messages`, and `workspace` are
|
|
7835
|
+
* free: a `workspace` renders nothing its own `scope` omits. A refused job's finding
|
|
7836
|
+
* opens with `JOB_REFUSAL_PREFIX` and then the job's path from `job`; an exceeded member's opens
|
|
7837
|
+
* with that path, a dot, and the member. The walk is total: an unreadable job yields a finding,
|
|
7838
|
+
* never a throw. The `AgentFanout` class runs it on every attempt of a unit, before the unit spawns
|
|
7839
|
+
* or runs.
|
|
7840
|
+
*
|
|
7841
|
+
* @param root - The root job of the tree
|
|
7842
|
+
* @returns One finding per refused job and per exceeded member, in walk order
|
|
7843
|
+
*
|
|
7844
|
+
* @example
|
|
7845
|
+
* ```ts
|
|
7846
|
+
* scanJobTree({ provider: 'main', children: [{ provider: 'other' }] })
|
|
7847
|
+
* // ['job.children[0].provider: "other" differs from its parent\'s, which is "main"']
|
|
7848
|
+
* ```
|
|
7849
|
+
*/
|
|
7850
|
+
function scanJobTree(root) {
|
|
7851
|
+
const findings = [];
|
|
7852
|
+
const pending = [[
|
|
7853
|
+
root,
|
|
7854
|
+
"job",
|
|
7855
|
+
void 0
|
|
7856
|
+
]];
|
|
7857
|
+
for (const [job, path, parent] of pending) {
|
|
7858
|
+
if (!agentJobContract.is(job)) {
|
|
7859
|
+
const faults = (0, _orkestrel_contract.attempt)(() => agentJobContract.audit(job));
|
|
7860
|
+
findings.push(`${JOB_REFUSAL_PREFIX}${path}: ${faults.success ? (0, _orkestrel_contract.describeFaults)(faults.value, agentJobContract.schema) : "a member read threw, so the job cannot be read"}`);
|
|
7861
|
+
continue;
|
|
7862
|
+
}
|
|
7863
|
+
const refusals = collectJobRefusals(job);
|
|
7864
|
+
if (refusals.length > 0) {
|
|
7865
|
+
findings.push(...refusals.map((line) => `${JOB_REFUSAL_PREFIX}${path}.${line}`));
|
|
7866
|
+
continue;
|
|
7867
|
+
}
|
|
7868
|
+
if (parent !== void 0) {
|
|
7869
|
+
const held = parent.tools ?? [];
|
|
7870
|
+
const outside = (job.tools ?? []).filter((name) => !held.includes(name));
|
|
7871
|
+
const enabled = parent.builtins ?? [];
|
|
7872
|
+
const extra = (job.builtins ?? []).filter((name) => !enabled.includes(name));
|
|
7873
|
+
const unlisted = [
|
|
7874
|
+
"skills",
|
|
7875
|
+
"memos",
|
|
7876
|
+
"workspaces"
|
|
7877
|
+
].map((list) => [list, (job.scope?.[list] ?? []).filter((name) => !(parent.scope?.[list] ?? []).includes(name))]);
|
|
7878
|
+
const limit = job.limit ?? 10;
|
|
7879
|
+
const bound = parent.limit ?? 10;
|
|
7880
|
+
findings.push(...[
|
|
7881
|
+
outside.length === 0 ? void 0 : `${path}.tools: names ${outside.map((name) => (0, _orkestrel_contract.preview)(name)).join(", ")}, which its parent does not hold`,
|
|
7882
|
+
extra.length === 0 ? void 0 : `${path}.builtins: names ${extra.map((name) => (0, _orkestrel_contract.preview)(name)).join(", ")}, which its parent does not hold`,
|
|
7883
|
+
...unlisted.map(([list, names]) => names.length === 0 ? void 0 : `${path}.scope.${list}: names ${names.map((name) => (0, _orkestrel_contract.preview)(name)).join(", ")}, which its parent does not hold`),
|
|
7884
|
+
job.authority === void 0 || job.authority === parent.authority ? void 0 : `${path}.authority: ${(0, _orkestrel_contract.preview)(job.authority)} is neither absent nor its parent's, which is ${parent.authority === void 0 ? "none" : (0, _orkestrel_contract.preview)(parent.authority)}`,
|
|
7885
|
+
job.provider === parent.provider ? void 0 : `${path}.provider: ${(0, _orkestrel_contract.preview)(job.provider)} differs from its parent's, which is ${(0, _orkestrel_contract.preview)(parent.provider)}`,
|
|
7886
|
+
job.scheduler === void 0 || job.scheduler === parent.scheduler ? void 0 : `${path}.scheduler: ${(0, _orkestrel_contract.preview)(job.scheduler)} is neither absent nor its parent's, which is ${parent.scheduler === void 0 ? "none" : (0, _orkestrel_contract.preview)(parent.scheduler)}`,
|
|
7887
|
+
limit <= bound ? void 0 : `${path}.limit: ${job.limit === void 0 ? `none reads ${limit}, which exceeds` : `${limit} exceeds`} its parent's ${bound}`,
|
|
7888
|
+
parent.timeout === void 0 || job.timeout !== void 0 && job.timeout <= parent.timeout ? void 0 : `${path}.timeout: ${job.timeout ?? "none"} exceeds its parent's ${parent.timeout}`,
|
|
7889
|
+
parent.budget === void 0 || job.budget !== void 0 && job.budget <= parent.budget ? void 0 : `${path}.budget: ${job.budget ?? "none"} exceeds its parent's ${parent.budget}`
|
|
7890
|
+
].filter((finding) => finding !== void 0));
|
|
7891
|
+
}
|
|
7892
|
+
for (const [index, child] of (job.children ?? []).entries()) pending.push([
|
|
7893
|
+
child,
|
|
7894
|
+
`${path}.children[${index}]`,
|
|
7895
|
+
job
|
|
7896
|
+
]);
|
|
7897
|
+
}
|
|
7898
|
+
return findings;
|
|
7899
|
+
}
|
|
7900
|
+
/**
|
|
7084
7901
|
* Assembles the settled {@link AgentResult} from a run's {@link AgentRunResult} — `thinking`,
|
|
7085
7902
|
* `usage`, and `stop` are carried only when the run surfaced them, and the loop-internal
|
|
7086
7903
|
* `exhausted` flag is left out.
|
|
@@ -7163,7 +7980,7 @@ function denyCall(call, reason) {
|
|
|
7163
7980
|
function renderArguments(value) {
|
|
7164
7981
|
const rendered = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.canonicalStringify)(value));
|
|
7165
7982
|
if (!rendered.success || rendered.value === void 0) return void 0;
|
|
7166
|
-
return rendered.value.replace(
|
|
7983
|
+
return rendered.value.replace(new RegExp(ARGUMENT_CONTROL_PATTERN.source, "gu"), (match) => Array.from({ length: match.length }, (_, index) => `\\u${match.charCodeAt(index).toString(16).padStart(4, "0")}`).join(""));
|
|
7167
7984
|
}
|
|
7168
7985
|
/**
|
|
7169
7986
|
* Applies the `consequential` floor to one {@link AuthorityDecision} — an `allow` for a tool
|
|
@@ -7382,6 +8199,37 @@ function requireConversation(value) {
|
|
|
7382
8199
|
});
|
|
7383
8200
|
}
|
|
7384
8201
|
/**
|
|
8202
|
+
* Returns a scope a run can bind, or throws a `ContractError` coded `'placement'`; `undefined`
|
|
8203
|
+
* passes through.
|
|
8204
|
+
*
|
|
8205
|
+
* @remarks
|
|
8206
|
+
* The check is {@link isScope}, a total guard that reads each member one time and admits any
|
|
8207
|
+
* implementation of `ScopeInterface`. `undefined` passes, because an absent option leaves the
|
|
8208
|
+
* context scope alone as the run's ceiling. The error's `context` carries the option path, the
|
|
8209
|
+
* limit `'ScopeInterface'`, and a preview of the refused value.
|
|
8210
|
+
*
|
|
8211
|
+
* @param value - The run's `scope` option, or `undefined`
|
|
8212
|
+
* @returns The value unchanged
|
|
8213
|
+
* @throws {ContractError} Thrown when `value` is defined and does not implement `ScopeInterface` (code `'placement'`)
|
|
8214
|
+
*
|
|
8215
|
+
* @example
|
|
8216
|
+
* ```ts
|
|
8217
|
+
* requireScope(createScope({ name: 'reader', tools: ['search'] })) // the scope
|
|
8218
|
+
* requireScope({ id: 's1', name: 'reader' }) // throws ContractError ('placement')
|
|
8219
|
+
* ```
|
|
8220
|
+
*/
|
|
8221
|
+
function requireScope(value) {
|
|
8222
|
+
if (value === void 0 || isScope(value)) return value;
|
|
8223
|
+
throw new _orkestrel_contract.ContractError("Agent: scope must implement ScopeInterface", {
|
|
8224
|
+
code: "placement",
|
|
8225
|
+
context: {
|
|
8226
|
+
path: ["options", "scope"],
|
|
8227
|
+
limit: "ScopeInterface",
|
|
8228
|
+
received: (0, _orkestrel_contract.preview)(value)
|
|
8229
|
+
}
|
|
8230
|
+
});
|
|
8231
|
+
}
|
|
8232
|
+
/**
|
|
7385
8233
|
* Renders a tool's output as the string content of its tool message, or returns `undefined` when JSON
|
|
7386
8234
|
* cannot carry it.
|
|
7387
8235
|
*
|
|
@@ -7483,6 +8331,735 @@ function extractQueueOptions(options) {
|
|
|
7483
8331
|
...timeout === void 0 ? {} : { timeout }
|
|
7484
8332
|
};
|
|
7485
8333
|
}
|
|
8334
|
+
/**
|
|
8335
|
+
* Collects the definitions of the tools an agent serves itself, as a run advertises them.
|
|
8336
|
+
*
|
|
8337
|
+
* @remarks
|
|
8338
|
+
* Each definition carries the name, description, and annotations `BUILTIN_TOOLS` declares and the
|
|
8339
|
+
* parameters of the built-in's compiled contract in `builtinToolContracts`, in the order `builtins` lists them, so
|
|
8340
|
+
* it equals the definition of the tool `createBuiltinTool` creates. A relay server lists these
|
|
8341
|
+
* among its declared definitions to forward a page's built-ins. A name that is no built-in
|
|
8342
|
+
* contributes nothing.
|
|
8343
|
+
*
|
|
8344
|
+
* @param options - The agent options whose `builtins` to project
|
|
8345
|
+
* @returns The definitions, empty when `builtins` is absent
|
|
8346
|
+
*
|
|
8347
|
+
* @example
|
|
8348
|
+
* ```ts
|
|
8349
|
+
* collectBuiltinDefinitions({ builtins: ['load', 'unload'] }).map((definition) => definition.name) // ['load', 'unload']
|
|
8350
|
+
* ```
|
|
8351
|
+
*/
|
|
8352
|
+
function collectBuiltinDefinitions(options) {
|
|
8353
|
+
return (options.builtins ?? []).filter(isAgentBuiltin).map((name) => {
|
|
8354
|
+
const declared = BUILTIN_TOOLS[name];
|
|
8355
|
+
const parameters = (0, _orkestrel_contract.schemaToParameters)(builtinToolContracts[name].contract.schema);
|
|
8356
|
+
return {
|
|
8357
|
+
name,
|
|
8358
|
+
description: declared.description,
|
|
8359
|
+
...parameters === void 0 ? {} : { parameters },
|
|
8360
|
+
...declared.annotations === void 0 ? {} : { annotations: declared.annotations }
|
|
8361
|
+
};
|
|
8362
|
+
});
|
|
8363
|
+
}
|
|
8364
|
+
/**
|
|
8365
|
+
* Returns an agent's `builtins` option as an owned, frozen list, or throws a coded error before the
|
|
8366
|
+
* agent is built.
|
|
8367
|
+
*
|
|
8368
|
+
* @remarks
|
|
8369
|
+
* `undefined` returns an empty list. The option must be an array of distinct {@link AgentBuiltin}
|
|
8370
|
+
* names; `unload` needs `load`, because only a loaded skill can be unloaded; and the host's tool
|
|
8371
|
+
* manager may hold no tool under an enabled name, because the run serves that name itself.
|
|
8372
|
+
*
|
|
8373
|
+
* @param value - The `builtins` option
|
|
8374
|
+
* @param tools - The host's tool manager
|
|
8375
|
+
* @returns The enabled names, in the order given
|
|
8376
|
+
* @throws {AgentError} Thrown when the option is not an array, names something that is no built-in, repeats a name, lists `unload` without `load`, or names a tool the manager holds (code `'BUILTIN'`)
|
|
8377
|
+
*
|
|
8378
|
+
* @example
|
|
8379
|
+
* ```ts
|
|
8380
|
+
* requireBuiltins(['load', 'unload'], createToolManager()) // ['load', 'unload']
|
|
8381
|
+
* requireBuiltins(['unload'], createToolManager()) // throws AgentError ('BUILTIN')
|
|
8382
|
+
* ```
|
|
8383
|
+
*/
|
|
8384
|
+
function requireBuiltins(value, tools) {
|
|
8385
|
+
if (value === void 0) return Object.freeze([]);
|
|
8386
|
+
if (!(0, _orkestrel_contract.isArray)(value)) throw new AgentError("BUILTIN", `builtins must be an array; received ${(0, _orkestrel_contract.preview)(value)}`);
|
|
8387
|
+
const names = [...value].filter(isAgentBuiltin);
|
|
8388
|
+
if (names.length !== value.length) throw new AgentError("BUILTIN", "builtins lists a name that is no built-in; list only 'load', 'unload', 'memo', 'remember', and 'switch'", { context: { received: (0, _orkestrel_contract.preview)(value) } });
|
|
8389
|
+
if (new Set(names).size !== names.length) throw new AgentError("BUILTIN", "builtins lists a name more than once", { context: { builtins: names } });
|
|
8390
|
+
if (names.includes("unload") && !names.includes("load")) throw new AgentError("BUILTIN", "builtins lists 'unload' without 'load'; add 'load' too", { context: { builtins: names } });
|
|
8391
|
+
const taken = names.filter((name) => tools.tool(name) !== void 0);
|
|
8392
|
+
if (taken.length > 0) throw new AgentError("BUILTIN", `the tool manager holds a tool under the built-in name ${taken.map((name) => `'${name}'`).join(", ")}; rename that tool or leave the name out of builtins`, { context: {
|
|
8393
|
+
builtins: names,
|
|
8394
|
+
taken
|
|
8395
|
+
} });
|
|
8396
|
+
return Object.freeze(names);
|
|
8397
|
+
}
|
|
8398
|
+
/**
|
|
8399
|
+
* Returns a `workspace` option unchanged, or throws a `ContractError` coded `'placement'`;
|
|
8400
|
+
* `undefined` passes through.
|
|
8401
|
+
*
|
|
8402
|
+
* @remarks
|
|
8403
|
+
* The check is `isString`. An id the workspace registry does not hold, or the ceiling does not
|
|
8404
|
+
* admit, passes here and renders nothing. The error's `context` carries the option path, the limit
|
|
8405
|
+
* `'string'`, and a preview of the refused value.
|
|
8406
|
+
*
|
|
8407
|
+
* @param value - The `workspace` option of an agent, a run, or a ledger's agent, or `undefined`
|
|
8408
|
+
* @returns The value unchanged
|
|
8409
|
+
* @throws {ContractError} Thrown when `value` is defined and is not a string (code `'placement'`)
|
|
8410
|
+
*
|
|
8411
|
+
* @example
|
|
8412
|
+
* ```ts
|
|
8413
|
+
* requireWorkspace('orders') // 'orders'
|
|
8414
|
+
* requireWorkspace(7) // throws ContractError ('placement')
|
|
8415
|
+
* ```
|
|
8416
|
+
*/
|
|
8417
|
+
function requireWorkspace(value) {
|
|
8418
|
+
if (value === void 0 || (0, _orkestrel_contract.isString)(value)) return value;
|
|
8419
|
+
throw new _orkestrel_contract.ContractError("Agent: workspace must be a string", {
|
|
8420
|
+
code: "placement",
|
|
8421
|
+
context: {
|
|
8422
|
+
path: ["options", "workspace"],
|
|
8423
|
+
limit: "string",
|
|
8424
|
+
received: (0, _orkestrel_contract.preview)(value)
|
|
8425
|
+
}
|
|
8426
|
+
});
|
|
8427
|
+
}
|
|
8428
|
+
/**
|
|
8429
|
+
* Returns the definitions a turn advertises from the allow-list, the registered skills, and the
|
|
8430
|
+
* shown skills.
|
|
8431
|
+
*
|
|
8432
|
+
* @remarks
|
|
8433
|
+
* The allow-list admits first, as `filterAllowList` does. When no skill is passed, or an allow-list
|
|
8434
|
+
* admits no `load` definition, the admitted list returns as it is. Otherwise a tool any registered
|
|
8435
|
+
* skill names is held back unless a shown skill names it too; `load` and `unload` are never held
|
|
8436
|
+
* back. A caller passes the registered skills only while `load` is enabled, so with no allow-list a
|
|
8437
|
+
* ceiling whose empty `skills` list withholds `load` from the candidates still holds the skills'
|
|
8438
|
+
* tools back. Holding a tool back is presentation, never authority: a call to it is denied as
|
|
8439
|
+
* outside the turn's scope.
|
|
8440
|
+
*
|
|
8441
|
+
* @param definitions - The candidate definitions, in advertisement order
|
|
8442
|
+
* @param names - The turn's `tools` allow-list (`undefined` ⇒ all, `[]` ⇒ none)
|
|
8443
|
+
* @param skills - The registered skills whose `tools` gate, or none while `load` is not enabled
|
|
8444
|
+
* @param shown - The names of the skills the turn shows
|
|
8445
|
+
* @returns The advertised definitions, in candidate order
|
|
8446
|
+
*
|
|
8447
|
+
* @example
|
|
8448
|
+
* ```ts
|
|
8449
|
+
* const skills = [{ id: '1', name: 'refunds', description: 'Refunds', content: '', tools: ['refund'], files: [] }]
|
|
8450
|
+
* filterAdvertisedTools([{ name: 'load' }, { name: 'refund' }], undefined, skills, []) // [{ name: 'load' }]
|
|
8451
|
+
* filterAdvertisedTools([{ name: 'load' }, { name: 'refund' }], undefined, skills, ['refunds']).length // 2
|
|
8452
|
+
* ```
|
|
8453
|
+
*/
|
|
8454
|
+
function filterAdvertisedTools(definitions, names, skills, shown) {
|
|
8455
|
+
const admitted = filterAllowList(names, definitions, (definition) => definition.name);
|
|
8456
|
+
if (skills.length === 0) return admitted;
|
|
8457
|
+
if (names !== void 0 && !admitted.some((definition) => definition.name === "load")) return admitted;
|
|
8458
|
+
const held = new Set(skills.flatMap((skill) => skill.tools));
|
|
8459
|
+
const visible = new Set(shown);
|
|
8460
|
+
const open = new Set(skills.filter((skill) => visible.has(skill.name)).flatMap((skill) => skill.tools));
|
|
8461
|
+
return admitted.filter((definition) => definition.name === "load" || definition.name === "unload" || !held.has(definition.name) || open.has(definition.name));
|
|
8462
|
+
}
|
|
8463
|
+
/**
|
|
8464
|
+
* Returns the definitions a run offers before the allow-list, without `remember` unless the
|
|
8465
|
+
* ceiling's `memos` list names a memo, without `switch` unless its `workspaces` list names a
|
|
8466
|
+
* workspace, and without `load` and `unload` while its `skills` list is empty.
|
|
8467
|
+
*
|
|
8468
|
+
* @remarks
|
|
8469
|
+
* `remember` writes and `switch` selects only a name its list holds, so an absent list and an empty
|
|
8470
|
+
* list each leave the built-in nothing to act on, and it is not advertised. An absent `skills` list
|
|
8471
|
+
* admits every registered skill, so `load` and `unload` are withheld only when the list is empty.
|
|
8472
|
+
* Every other definition keeps its place.
|
|
8473
|
+
*
|
|
8474
|
+
* @param definitions - The candidate definitions, in advertisement order
|
|
8475
|
+
* @param ceiling - The turn's ceiling
|
|
8476
|
+
* @returns The definitions, in candidate order
|
|
8477
|
+
*
|
|
8478
|
+
* @example
|
|
8479
|
+
* ```ts
|
|
8480
|
+
* filterBuiltinCandidates([{ name: 'memo' }, { name: 'remember' }], { memos: [] }) // [{ name: 'memo' }]
|
|
8481
|
+
* filterBuiltinCandidates([{ name: 'memo' }, { name: 'remember' }], { memos: ['tone'] }).length // 2
|
|
8482
|
+
* ```
|
|
8483
|
+
*/
|
|
8484
|
+
function filterBuiltinCandidates(definitions, ceiling) {
|
|
8485
|
+
const writable = (ceiling.memos ?? []).length > 0;
|
|
8486
|
+
const selectable = (ceiling.workspaces ?? []).length > 0;
|
|
8487
|
+
const loadable = ceiling.skills === void 0 || ceiling.skills.length > 0;
|
|
8488
|
+
return definitions.filter((definition) => (definition.name !== "remember" || writable) && (definition.name !== "switch" || selectable) && (definition.name !== "load" && definition.name !== "unload" || loadable));
|
|
8489
|
+
}
|
|
8490
|
+
/**
|
|
8491
|
+
* Returns the refusal that names a subject and at most `BUILTIN_LIMITS.candidates` of the names a
|
|
8492
|
+
* caller can use.
|
|
8493
|
+
*
|
|
8494
|
+
* @remarks
|
|
8495
|
+
* The subject reads after "No", so it names what the caller may use, such as `'skill you can load'`.
|
|
8496
|
+
* The text echoes no argument, so an absent name and a name outside the ceiling read byte for byte
|
|
8497
|
+
* the same. Each name is JSON-encoded and capped by `preview`, in the order given. Past the cap one
|
|
8498
|
+
* fixed sentence sends the reader to the index in its prompt.
|
|
8499
|
+
*
|
|
8500
|
+
* @param subject - What the caller may name, such as `'skill you can load'`
|
|
8501
|
+
* @param names - The names the caller can use, in registry order
|
|
8502
|
+
* @returns The refusal text
|
|
8503
|
+
*
|
|
8504
|
+
* @example
|
|
8505
|
+
* ```ts
|
|
8506
|
+
* describeCandidates('skill you can load', ['refunds', 'billing'])
|
|
8507
|
+
* // 'No skill you can load has that name. Use one of: "refunds", "billing".'
|
|
8508
|
+
* ```
|
|
8509
|
+
*/
|
|
8510
|
+
function describeCandidates(subject, names) {
|
|
8511
|
+
if (names.length === 0) return `No ${subject} has that name, and none is available now.`;
|
|
8512
|
+
return `No ${subject} has that name. Use one of: ${names.slice(0, BUILTIN_LIMITS.candidates).map((name) => (0, _orkestrel_contract.preview)(name)).join(", ")}.${names.length > BUILTIN_LIMITS.candidates ? " The index in your prompt lists the others." : ""}`;
|
|
8513
|
+
}
|
|
8514
|
+
/**
|
|
8515
|
+
* Checks whether a text is one nonblank line: it holds a code point other than white space and
|
|
8516
|
+
* default-ignorable code points (`ARGUMENT_BLANK_PATTERN`), and no control, format, line-separator, or
|
|
8517
|
+
* paragraph-separator code point (`ARGUMENT_CONTROL_PATTERN`).
|
|
8518
|
+
*
|
|
8519
|
+
* @remarks
|
|
8520
|
+
* The class is the one `renderArguments` encodes. A memo's name and description render in the memos
|
|
8521
|
+
* index one line each, so `remember` refuses a value outside this check.
|
|
8522
|
+
*
|
|
8523
|
+
* @param text - The text to check
|
|
8524
|
+
* @returns True if the text is one nonblank line; false otherwise
|
|
8525
|
+
*
|
|
8526
|
+
* @example
|
|
8527
|
+
* ```ts
|
|
8528
|
+
* matchesPlainLine('Reply tone') // true
|
|
8529
|
+
* matchesPlainLine('Reply\ntone') // false
|
|
8530
|
+
* matchesPlainLine(' ') // false
|
|
8531
|
+
* ```
|
|
8532
|
+
*/
|
|
8533
|
+
function matchesPlainLine(text) {
|
|
8534
|
+
return !ARGUMENT_BLANK_PATTERN.test(text) && !ARGUMENT_CONTROL_PATTERN.test(text);
|
|
8535
|
+
}
|
|
8536
|
+
/**
|
|
8537
|
+
* Checks whether two ceilings carry the same lists of what a head renders: `instructions`,
|
|
8538
|
+
* `skills`, `memos`, `files`, and `workspaces`.
|
|
8539
|
+
*
|
|
8540
|
+
* @remarks
|
|
8541
|
+
* Each list compares by its members in order, and an absent list equals only an absent list,
|
|
8542
|
+
* because `undefined` admits everything and `[]` admits nothing. The `tools` list is not compared:
|
|
8543
|
+
* it reaches the tools block, never the head.
|
|
8544
|
+
*
|
|
8545
|
+
* @param left - One ceiling's lists
|
|
8546
|
+
* @param right - The other ceiling's lists
|
|
8547
|
+
* @returns True if every compared list matches; false otherwise
|
|
8548
|
+
*
|
|
8549
|
+
* @example
|
|
8550
|
+
* ```ts
|
|
8551
|
+
* matchesHeadLists({ skills: ['refunds'] }, { skills: ['refunds'], tools: [] }) // true
|
|
8552
|
+
* matchesHeadLists({ skills: [] }, {}) // false
|
|
8553
|
+
* ```
|
|
8554
|
+
*/
|
|
8555
|
+
function matchesHeadLists(left, right) {
|
|
8556
|
+
return [
|
|
8557
|
+
[left.instructions, right.instructions],
|
|
8558
|
+
[left.skills, right.skills],
|
|
8559
|
+
[left.memos, right.memos],
|
|
8560
|
+
[left.files, right.files],
|
|
8561
|
+
[left.workspaces, right.workspaces]
|
|
8562
|
+
].every(([one, other]) => one === void 0 || other === void 0 ? one === other : one.length === other.length && one.every((key, index) => key === other[index]));
|
|
8563
|
+
}
|
|
8564
|
+
/**
|
|
8565
|
+
* Resolves what a turn shows: the conversation's record, and the given workspace, within the
|
|
8566
|
+
* ceiling and the registries.
|
|
8567
|
+
*
|
|
8568
|
+
* @remarks
|
|
8569
|
+
* The caller reads the ceiling, so a turn reads each scope list one time. The skills are those
|
|
8570
|
+
* {@link collectDisclosure} reads from the conversation, in load order, keeping each that the skill
|
|
8571
|
+
* registry holds and the ceiling's `skills` list admits; an unregistered or excluded name shows
|
|
8572
|
+
* nothing. The workspace is the conversation's last switch, else the given one, kept only when the
|
|
8573
|
+
* workspace registry holds it and the ceiling's `workspaces` list admits it. The record counts only
|
|
8574
|
+
* the built-ins in `builtins`, as {@link collectDisclosure} states.
|
|
8575
|
+
*
|
|
8576
|
+
* @param context - The agent's context, whose scope, skills, and workspaces it reads
|
|
8577
|
+
* @param conversation - The conversation whose record to read
|
|
8578
|
+
* @param workspace - The `workspace` option of the run or the agent, or `undefined`
|
|
8579
|
+
* @param ceiling - The ceiling the turn read: the context scope intersected with the run scope
|
|
8580
|
+
* @param builtins - The built-ins the run enables
|
|
8581
|
+
* @returns The shown skills, in order, and the shown workspace when one is
|
|
8582
|
+
*
|
|
8583
|
+
* @example
|
|
8584
|
+
* ```ts
|
|
8585
|
+
* resolveDisclosure(agent.context, agent.context.messages, 'orders', {}, []) // { skills: [], workspace: 'orders' } when orders is registered
|
|
8586
|
+
* ```
|
|
8587
|
+
*/
|
|
8588
|
+
function resolveDisclosure(context, conversation, workspace, ceiling, builtins) {
|
|
8589
|
+
const recorded = collectDisclosure(conversation, builtins);
|
|
8590
|
+
const admitted = new Set(filterAllowList(ceiling.skills, context.skills.skills(), (skill) => skill.name).map((skill) => skill.name));
|
|
8591
|
+
const skills = recorded.skills.filter((name) => admitted.has(name));
|
|
8592
|
+
const selected = recorded.workspace ?? workspace;
|
|
8593
|
+
return selected !== void 0 && context.workspaces.workspace(selected) !== void 0 && filterAllowList(ceiling.workspaces, [selected], (id) => id).length > 0 ? {
|
|
8594
|
+
skills,
|
|
8595
|
+
workspace: selected
|
|
8596
|
+
} : { skills };
|
|
8597
|
+
}
|
|
8598
|
+
/**
|
|
8599
|
+
* Collects the loaded skills and the selected workspace a conversation's stored built-in calls
|
|
8600
|
+
* record, counting only the built-ins a run enables.
|
|
8601
|
+
*
|
|
8602
|
+
* @remarks
|
|
8603
|
+
* Pure. It reads each section's retained originals, oldest first, then the live tail, and pairs
|
|
8604
|
+
* each tool message with the call that owns it through {@link collectToolGroups} and
|
|
8605
|
+
* {@link findToolCall}. It then folds the pairs in message order: a `load` whose message content
|
|
8606
|
+
* equals `BUILTIN_NOTES.loaded` adds its `skill`, an `unload` with `BUILTIN_NOTES.unloaded` removes
|
|
8607
|
+
* it, and a `switch` with `BUILTIN_NOTES.switched` selects its `workspace`. Each call's arguments
|
|
8608
|
+
* are admitted through the built-in's contract in `builtinToolContracts`, lossless repairs
|
|
8609
|
+
* included, as its tool's door admits them, so a `{ skill: 1 }` the door repaired to `'1'` counts
|
|
8610
|
+
* as `'1'`; arguments the contract refuses change nothing. A skill loaded again while shown keeps
|
|
8611
|
+
* its place, and one loaded again after its unload goes last. Any other message and a denied or
|
|
8612
|
+
* refused call change nothing. The result holds `workspace` only when a switch succeeded.
|
|
8613
|
+
*
|
|
8614
|
+
* A record counts only while its built-in is in `builtins`, so a host tool that holds a built-in's
|
|
8615
|
+
* name on a run that does not enable it shows nothing; with none of `load`, `unload`, and `switch`
|
|
8616
|
+
* enabled, nothing is read. A stored record cannot tell its writer, so one a host tool wrote under a
|
|
8617
|
+
* built-in's name while that built-in was off counts after a run enables it, bounded by the registry
|
|
8618
|
+
* and the ceiling as every record is.
|
|
8619
|
+
*
|
|
8620
|
+
* @param conversation - The conversation whose stored messages record the calls
|
|
8621
|
+
* @param builtins - The built-ins the run enables
|
|
8622
|
+
* @returns The skills in load order and the last selected workspace
|
|
8623
|
+
*
|
|
8624
|
+
* @example
|
|
8625
|
+
* ```ts
|
|
8626
|
+
* import { collectDisclosure, createConversation } from '@orkestrel/agent'
|
|
8627
|
+
*
|
|
8628
|
+
* collectDisclosure(createConversation(), ['load', 'unload']) // { skills: [] }
|
|
8629
|
+
* ```
|
|
8630
|
+
*/
|
|
8631
|
+
function collectDisclosure(conversation, builtins) {
|
|
8632
|
+
const counted = new Set(builtins.filter((name) => name === "load" || name === "unload" || name === "switch"));
|
|
8633
|
+
if (counted.size === 0) return { skills: [] };
|
|
8634
|
+
const stored = [...conversation.sections.flatMap((section) => section.messages), ...conversation.messages()];
|
|
8635
|
+
const owners = new Map(collectToolGroups(stored).flatMap((group) => group.flatMap((message) => {
|
|
8636
|
+
const call = findToolCall(group, message);
|
|
8637
|
+
return call === void 0 ? [] : [[message, call]];
|
|
8638
|
+
})));
|
|
8639
|
+
return stored.reduce((shown, message) => {
|
|
8640
|
+
const call = owners.get(message);
|
|
8641
|
+
const name = call?.name;
|
|
8642
|
+
if (call === void 0 || !isAgentBuiltin(name) || !counted.has(name)) return shown;
|
|
8643
|
+
const note = name === "load" ? BUILTIN_NOTES.loaded : name === "unload" ? BUILTIN_NOTES.unloaded : BUILTIN_NOTES.switched;
|
|
8644
|
+
if (message.content !== note) return shown;
|
|
8645
|
+
const admitted = (0, _orkestrel_contract.admitValue)(builtinToolContracts[name].contract, { ...call.arguments });
|
|
8646
|
+
const skill = admitted.success ? admitted.value.value["skill"] : void 0;
|
|
8647
|
+
const workspace = admitted.success ? admitted.value.value["workspace"] : void 0;
|
|
8648
|
+
if (name === "load") return !(0, _orkestrel_contract.isString)(skill) || shown.skills.includes(skill) ? shown : {
|
|
8649
|
+
...shown,
|
|
8650
|
+
skills: [...shown.skills, skill]
|
|
8651
|
+
};
|
|
8652
|
+
if (name === "unload") return !(0, _orkestrel_contract.isString)(skill) ? shown : {
|
|
8653
|
+
...shown,
|
|
8654
|
+
skills: shown.skills.filter((one) => one !== skill)
|
|
8655
|
+
};
|
|
8656
|
+
return !(0, _orkestrel_contract.isString)(workspace) ? shown : {
|
|
8657
|
+
skills: shown.skills,
|
|
8658
|
+
workspace
|
|
8659
|
+
};
|
|
8660
|
+
}, { skills: [] });
|
|
8661
|
+
}
|
|
8662
|
+
// src/core/agents/AgentRun.ts
|
|
8663
|
+
/**
|
|
8664
|
+
* Holds one agent run's own tools and the view its turns and builds share: the ceiling a turn
|
|
8665
|
+
* read, what it shows, what it advertises, and what the working head was built with.
|
|
8666
|
+
*
|
|
8667
|
+
* @remarks
|
|
8668
|
+
* `Agent` creates one per run, so no public signature names this class. The run's built-in tools
|
|
8669
|
+
* live in a tool manager the run owns and never changes after construction, so a tool a host
|
|
8670
|
+
* registers under a built-in's name is never read: `tool` and `execute` answer a built-in's name
|
|
8671
|
+
* from the run's manager and every other name from the host's. Each handler reads the ceiling when
|
|
8672
|
+
* it runs, the context scope intersected with the run scope at that moment, never a turn's copy.
|
|
8673
|
+
* The `caller` each call receives reads the ceiling the same way, as the call starts.
|
|
8674
|
+
*/
|
|
8675
|
+
var AgentRun = class {
|
|
8676
|
+
#context;
|
|
8677
|
+
#conversation;
|
|
8678
|
+
#scope;
|
|
8679
|
+
#workspace;
|
|
8680
|
+
#enabled;
|
|
8681
|
+
#builtins;
|
|
8682
|
+
#tools;
|
|
8683
|
+
#ceiling = {};
|
|
8684
|
+
#shown = { skills: [] };
|
|
8685
|
+
#ids = [];
|
|
8686
|
+
#files = [];
|
|
8687
|
+
#advertised = [];
|
|
8688
|
+
#snapshot;
|
|
8689
|
+
#selection;
|
|
8690
|
+
#length = 0;
|
|
8691
|
+
#head;
|
|
8692
|
+
/**
|
|
8693
|
+
* Creates the run's own tools for the enabled built-ins.
|
|
8694
|
+
*
|
|
8695
|
+
* @param context - The agent's context
|
|
8696
|
+
* @param conversation - The conversation the run is bound to
|
|
8697
|
+
* @param scope - The run scope, or `undefined`
|
|
8698
|
+
* @param workspace - The workspace the run renders in place of the active one, already checked, or `undefined`
|
|
8699
|
+
* @param builtins - The enabled built-ins, already checked
|
|
8700
|
+
*/
|
|
8701
|
+
constructor(context, conversation, scope, workspace, builtins) {
|
|
8702
|
+
this.#context = context;
|
|
8703
|
+
this.#conversation = conversation;
|
|
8704
|
+
this.#scope = scope;
|
|
8705
|
+
this.#workspace = workspace;
|
|
8706
|
+
this.#enabled = builtins;
|
|
8707
|
+
this.#builtins = new Set(builtins);
|
|
8708
|
+
this.#tools = (0, _orkestrel_tool.createToolManager)();
|
|
8709
|
+
this.#tools.add(builtins.map((name) => createBuiltinTool(name, (args) => this.#handle(name, args))));
|
|
8710
|
+
}
|
|
8711
|
+
/**
|
|
8712
|
+
* Returns the conversation the run is bound to.
|
|
8713
|
+
*
|
|
8714
|
+
* @returns The conversation
|
|
8715
|
+
*/
|
|
8716
|
+
get conversation() {
|
|
8717
|
+
return this.#conversation;
|
|
8718
|
+
}
|
|
8719
|
+
/**
|
|
8720
|
+
* Returns the run scope.
|
|
8721
|
+
*
|
|
8722
|
+
* @returns The scope, or undefined when the run has none
|
|
8723
|
+
*/
|
|
8724
|
+
get scope() {
|
|
8725
|
+
return this.#scope;
|
|
8726
|
+
}
|
|
8727
|
+
/**
|
|
8728
|
+
* Returns an owned scope holding the run scope's lists as the last `observe` read them, so a build
|
|
8729
|
+
* renders by the turn's one read and never reads the run scope again.
|
|
8730
|
+
*
|
|
8731
|
+
* @returns The owned scope, or undefined when the run has none
|
|
8732
|
+
*/
|
|
8733
|
+
get snapshot() {
|
|
8734
|
+
return this.#snapshot;
|
|
8735
|
+
}
|
|
8736
|
+
/**
|
|
8737
|
+
* Returns the ceiling the last `observe` read: the context scope intersected with the run scope.
|
|
8738
|
+
*
|
|
8739
|
+
* @returns The ceiling's lists
|
|
8740
|
+
*/
|
|
8741
|
+
get ceiling() {
|
|
8742
|
+
return this.#ceiling;
|
|
8743
|
+
}
|
|
8744
|
+
/**
|
|
8745
|
+
* Returns the definitions the last `observe` advertised.
|
|
8746
|
+
*
|
|
8747
|
+
* @returns The advertised definitions
|
|
8748
|
+
*/
|
|
8749
|
+
get advertised() {
|
|
8750
|
+
return this.#advertised;
|
|
8751
|
+
}
|
|
8752
|
+
/**
|
|
8753
|
+
* Returns what a build renders from the last `observe`: the shown skills and workspace, or
|
|
8754
|
+
* undefined when it shows neither.
|
|
8755
|
+
*
|
|
8756
|
+
* @returns The shown value, or undefined
|
|
8757
|
+
*/
|
|
8758
|
+
get disclosure() {
|
|
8759
|
+
return this.#shown.skills.length === 0 && this.#shown.workspace === void 0 ? void 0 : this.#shown;
|
|
8760
|
+
}
|
|
8761
|
+
/**
|
|
8762
|
+
* Returns whether the last `observe` advertised `switch`, which lists the workspaces in the head.
|
|
8763
|
+
*
|
|
8764
|
+
* @returns True if `switch` is advertised; false otherwise
|
|
8765
|
+
*/
|
|
8766
|
+
get switchable() {
|
|
8767
|
+
return this.#advertised.some((definition) => definition.name === "switch");
|
|
8768
|
+
}
|
|
8769
|
+
/**
|
|
8770
|
+
* Returns what a tool call receives as `ToolContext.caller`: the workspace instance the working
|
|
8771
|
+
* head rendered, kept only while the ceiling at this moment admits its id, and that ceiling's
|
|
8772
|
+
* `files` list.
|
|
8773
|
+
*
|
|
8774
|
+
* @remarks
|
|
8775
|
+
* The instance is the one the head's build resolved, never the registry's `active` pointer at the
|
|
8776
|
+
* call, so a host's `switch` made while the provider answers reaches the next turn's head and not
|
|
8777
|
+
* this call. Each read of this getter reads the `workspaces` and `files` lists of the context
|
|
8778
|
+
* scope and the run scope one time each, and no other list, so a call reads them as it starts, a
|
|
8779
|
+
* narrowing made after the turn's decision reaches that call, and a turn still reads its `tools`
|
|
8780
|
+
* list once.
|
|
8781
|
+
*
|
|
8782
|
+
* @returns A fresh caller
|
|
8783
|
+
*/
|
|
8784
|
+
get caller() {
|
|
8785
|
+
const outer = this.#context.scope;
|
|
8786
|
+
const inner = this.#scope;
|
|
8787
|
+
const rendered = this.#head?.rendered;
|
|
8788
|
+
const [workspace] = filterAllowList(intersectKeys(outer?.workspaces, inner?.workspaces), rendered === void 0 ? [] : [rendered], (one) => one.id);
|
|
8789
|
+
return {
|
|
8790
|
+
workspace,
|
|
8791
|
+
files: intersectKeys(outer?.files, inner?.files)
|
|
8792
|
+
};
|
|
8793
|
+
}
|
|
8794
|
+
/**
|
|
8795
|
+
* Returns the selection the working head rendered.
|
|
8796
|
+
*
|
|
8797
|
+
* @returns The selection, or undefined before the first build
|
|
8798
|
+
*/
|
|
8799
|
+
get selection() {
|
|
8800
|
+
return this.#selection;
|
|
8801
|
+
}
|
|
8802
|
+
/**
|
|
8803
|
+
* Returns the length of the array the working head was built as.
|
|
8804
|
+
*
|
|
8805
|
+
* @returns The length, 0 before the first build
|
|
8806
|
+
*/
|
|
8807
|
+
get length() {
|
|
8808
|
+
return this.#length;
|
|
8809
|
+
}
|
|
8810
|
+
/**
|
|
8811
|
+
* Reads the ceiling, resolves what the run shows within it, and computes what it advertises.
|
|
8812
|
+
*
|
|
8813
|
+
* @remarks
|
|
8814
|
+
* The ceiling's lists are each read one time. The conversation's record is read only while
|
|
8815
|
+
* `load`, `unload`, or `switch` is enabled, and only the records of an enabled built-in count.
|
|
8816
|
+
* Registered skills hold their tools back only while `load` is enabled. The view compared with
|
|
8817
|
+
* the working head's is the ceiling's `instructions`, `skills`, `memos`, `files`, and
|
|
8818
|
+
* `workspaces` lists, the shown skills by id, the shown workspace, the files that workspace
|
|
8819
|
+
* renders under the ceiling's `files` list, compared by identity, and whether `switch` is
|
|
8820
|
+
* advertised. An edit to a rendered file, by a tool or by the host, therefore makes the head
|
|
8821
|
+
* stale, and an edit to a file no build renders does not.
|
|
8822
|
+
*
|
|
8823
|
+
* @returns True if the working head was built with another view and must be rebuilt; false otherwise
|
|
8824
|
+
*/
|
|
8825
|
+
observe() {
|
|
8826
|
+
const lists = this.#scope === void 0 ? void 0 : intersectFilters(void 0, this.#scope);
|
|
8827
|
+
const ceiling = intersectFilters(this.#context.scope, lists);
|
|
8828
|
+
this.#snapshot = lists === void 0 ? void 0 : createScope({
|
|
8829
|
+
name: "run",
|
|
8830
|
+
...lists
|
|
8831
|
+
});
|
|
8832
|
+
const shown = resolveDisclosure(this.#context, this.#conversation, this.#workspace, ceiling, this.#enabled);
|
|
8833
|
+
this.#ceiling = ceiling;
|
|
8834
|
+
this.#shown = shown;
|
|
8835
|
+
this.#ids = shown.skills.flatMap((name) => this.#context.skills.skill(name)?.id ?? []);
|
|
8836
|
+
this.#files = filterAllowList(ceiling.files, resolveWorkspace(this.#context.workspaces, shown.workspace, ceiling.workspaces)?.files() ?? [], (file) => file.path);
|
|
8837
|
+
this.#advertised = filterAdvertisedTools(this.#candidates(ceiling), ceiling.tools, this.#builtins.has("load") ? this.#context.skills.skills() : [], shown.skills);
|
|
8838
|
+
const head = this.#head;
|
|
8839
|
+
return head === void 0 || !matchesHeadLists(head.ceiling, ceiling) || head.ids.length !== this.#ids.length || head.ids.some((id, index) => id !== this.#ids[index]) || head.workspace !== shown.workspace || head.switchable !== this.switchable || head.files.length !== this.#files.length || head.files.some((file, index) => file !== this.#files[index]);
|
|
8840
|
+
}
|
|
8841
|
+
/**
|
|
8842
|
+
* Records what the working head was built with: the selection, the built array's length, the
|
|
8843
|
+
* view the last `observe` resolved, and the workspace instance the build rendered.
|
|
8844
|
+
*
|
|
8845
|
+
* @remarks
|
|
8846
|
+
* Call it right after the build: it resolves the rendered workspace from the inputs the build
|
|
8847
|
+
* read, the shown workspace and the context scope's `workspaces` list intersected with the
|
|
8848
|
+
* snapshot's.
|
|
8849
|
+
*
|
|
8850
|
+
* @param selection - The selection the build rendered
|
|
8851
|
+
* @param length - The length of the built array
|
|
8852
|
+
*/
|
|
8853
|
+
record(selection, length) {
|
|
8854
|
+
this.#selection = selection;
|
|
8855
|
+
this.#length = length;
|
|
8856
|
+
this.#head = {
|
|
8857
|
+
ceiling: this.#ceiling,
|
|
8858
|
+
ids: this.#ids,
|
|
8859
|
+
workspace: this.#shown.workspace,
|
|
8860
|
+
switchable: this.switchable,
|
|
8861
|
+
files: this.#files,
|
|
8862
|
+
rendered: resolveWorkspace(this.#context.workspaces, this.#shown.workspace, intersectKeys(this.#context.scope?.workspaces, this.#snapshot?.workspaces))
|
|
8863
|
+
};
|
|
8864
|
+
}
|
|
8865
|
+
/**
|
|
8866
|
+
* Finds the tool a call names: the run's own for a built-in's name, the host's for any other.
|
|
8867
|
+
*
|
|
8868
|
+
* @param name - The tool name
|
|
8869
|
+
* @returns The tool, or undefined when none is registered under the name
|
|
8870
|
+
*/
|
|
8871
|
+
tool(name) {
|
|
8872
|
+
return this.#builtins.has(name) ? this.#tools.tool(name) : this.#context.tools.tool(name);
|
|
8873
|
+
}
|
|
8874
|
+
/**
|
|
8875
|
+
* Executes one call through the run's manager for a built-in's name, and the host's for any other.
|
|
8876
|
+
*
|
|
8877
|
+
* @param call - The call to execute
|
|
8878
|
+
* @param context - The execution context
|
|
8879
|
+
* @returns The correlated result
|
|
8880
|
+
*/
|
|
8881
|
+
execute(call, context) {
|
|
8882
|
+
return this.#builtins.has(call.name) ? this.#tools.execute(call, context) : this.#context.tools.execute(call, context);
|
|
8883
|
+
}
|
|
8884
|
+
#candidates(ceiling) {
|
|
8885
|
+
return [...this.#context.tools.definitions().filter((one) => !this.#builtins.has(one.name)), ...filterBuiltinCandidates(this.#tools.definitions(), ceiling)];
|
|
8886
|
+
}
|
|
8887
|
+
#live() {
|
|
8888
|
+
return intersectFilters(this.#context.scope, this.#scope);
|
|
8889
|
+
}
|
|
8890
|
+
#handle(name, args) {
|
|
8891
|
+
switch (name) {
|
|
8892
|
+
case "load": return this.#load(args.skill);
|
|
8893
|
+
case "unload": return this.#unload(args.skill);
|
|
8894
|
+
case "memo": return this.#memo(args.name);
|
|
8895
|
+
case "remember": return this.#remember(args.name, args.description, args.content);
|
|
8896
|
+
case "switch": return this.#switch(args.workspace);
|
|
8897
|
+
}
|
|
8898
|
+
}
|
|
8899
|
+
#load(skill) {
|
|
8900
|
+
const admitted = filterAllowList(this.#live().skills, this.#context.skills.skills(), (one) => one.name).map((one) => one.name);
|
|
8901
|
+
if (!(0, _orkestrel_contract.isString)(skill) || !admitted.includes(skill)) throw new _orkestrel_tool.ToolError("ARGUMENTS", describeCandidates("skill you can load", admitted));
|
|
8902
|
+
return BUILTIN_NOTES.loaded;
|
|
8903
|
+
}
|
|
8904
|
+
#unload(skill) {
|
|
8905
|
+
const loaded = collectDisclosure(this.#conversation, this.#enabled).skills;
|
|
8906
|
+
if (!(0, _orkestrel_contract.isString)(skill) || !loaded.includes(skill)) throw new _orkestrel_tool.ToolError("ARGUMENTS", describeCandidates("skill you loaded", loaded));
|
|
8907
|
+
return BUILTIN_NOTES.unloaded;
|
|
8908
|
+
}
|
|
8909
|
+
#memo(name) {
|
|
8910
|
+
const admitted = filterAllowList(this.#live().memos, this.#context.memos.memos(), (one) => one.name);
|
|
8911
|
+
const memo = admitted.find((one) => one.name === name);
|
|
8912
|
+
if (memo === void 0) throw new _orkestrel_tool.ToolError("ARGUMENTS", describeCandidates("memo you can read", admitted.map((one) => one.name)));
|
|
8913
|
+
return memo.content;
|
|
8914
|
+
}
|
|
8915
|
+
#remember(name, description, content) {
|
|
8916
|
+
const allowed = this.#live().memos;
|
|
8917
|
+
if (allowed === void 0 || allowed.length === 0) throw new _orkestrel_tool.ToolError("ARGUMENTS", "No memo can be written here: the host lists no memo name you may write. Continue without saving it.");
|
|
8918
|
+
if (!(0, _orkestrel_contract.isString)(name) || !allowed.includes(name)) throw new _orkestrel_tool.ToolError("ARGUMENTS", describeCandidates("memo you may write", allowed));
|
|
8919
|
+
if (!(0, _orkestrel_contract.isString)(description) || !(0, _orkestrel_contract.isString)(content)) throw new _orkestrel_tool.ToolError("ARGUMENTS", "Send description and content as text.");
|
|
8920
|
+
if (!matchesPlainLine(name) || !matchesPlainLine(description)) throw new _orkestrel_tool.ToolError("ARGUMENTS", "The name and the description must each be one line of visible text, with no line break or control character. Send them again on one line.");
|
|
8921
|
+
const current = this.#context.memos.memo(name);
|
|
8922
|
+
if (current !== void 0 && current.origin?.conversation !== this.#conversation.id) throw new _orkestrel_tool.ToolError("ARGUMENTS", "A memo with that name exists and this conversation did not write it, so it stays as it is. Read it with memo, or write a name you created.");
|
|
8923
|
+
this.#context.memos.add({
|
|
8924
|
+
name,
|
|
8925
|
+
description,
|
|
8926
|
+
content,
|
|
8927
|
+
origin: { conversation: this.#conversation.id }
|
|
8928
|
+
});
|
|
8929
|
+
return BUILTIN_NOTES.remembered;
|
|
8930
|
+
}
|
|
8931
|
+
#switch(workspace) {
|
|
8932
|
+
const allowed = this.#live().workspaces;
|
|
8933
|
+
if (allowed === void 0 || allowed.length === 0) throw new _orkestrel_tool.ToolError("ARGUMENTS", "No workspace can be selected here: the host lists no workspace you may select. Continue with the workspace you have.");
|
|
8934
|
+
const admitted = filterAllowList(allowed, this.#context.workspaces.workspaces(), (one) => one.id).map((one) => one.id);
|
|
8935
|
+
if (!(0, _orkestrel_contract.isString)(workspace) || !admitted.includes(workspace)) throw new _orkestrel_tool.ToolError("ARGUMENTS", describeCandidates("workspace you can select", admitted));
|
|
8936
|
+
return BUILTIN_NOTES.switched;
|
|
8937
|
+
}
|
|
8938
|
+
};
|
|
8939
|
+
// src/core/agents/ChannelCell.ts
|
|
8940
|
+
/**
|
|
8941
|
+
* Holds one value a `Channel` buffers and the cell pushed after it.
|
|
8942
|
+
*
|
|
8943
|
+
* @remarks
|
|
8944
|
+
* A `Channel` keeps its unread values as a chain of these cells, from its `#head` cell to its
|
|
8945
|
+
* `#tail` cell, so no public signature names this class. The `#next` field is a latch: the
|
|
8946
|
+
* `link` method sets it once, and a later call keeps the first cell.
|
|
8947
|
+
*/
|
|
8948
|
+
var ChannelCell = class {
|
|
8949
|
+
#value;
|
|
8950
|
+
#next;
|
|
8951
|
+
/**
|
|
8952
|
+
* Holds a value with no next cell.
|
|
8953
|
+
*
|
|
8954
|
+
* @param value - The value the cell holds
|
|
8955
|
+
*/
|
|
8956
|
+
constructor(value) {
|
|
8957
|
+
this.#value = value;
|
|
8958
|
+
}
|
|
8959
|
+
/**
|
|
8960
|
+
* Returns the value the cell holds.
|
|
8961
|
+
*
|
|
8962
|
+
* @returns The value
|
|
8963
|
+
*/
|
|
8964
|
+
get value() {
|
|
8965
|
+
return this.#value;
|
|
8966
|
+
}
|
|
8967
|
+
/**
|
|
8968
|
+
* Returns the cell pushed after this one.
|
|
8969
|
+
*
|
|
8970
|
+
* @returns The next cell, or undefined before one is linked
|
|
8971
|
+
*/
|
|
8972
|
+
get next() {
|
|
8973
|
+
return this.#next;
|
|
8974
|
+
}
|
|
8975
|
+
/**
|
|
8976
|
+
* Links the cell pushed after this one, once.
|
|
8977
|
+
*
|
|
8978
|
+
* @param cell - The cell pushed after this one
|
|
8979
|
+
*/
|
|
8980
|
+
link(cell) {
|
|
8981
|
+
this.#next ??= cell;
|
|
8982
|
+
}
|
|
8983
|
+
};
|
|
8984
|
+
// src/core/agents/Channel.ts
|
|
8985
|
+
/**
|
|
8986
|
+
* Buffers chunks in a minimal unbounded async channel — the eager pump writes them in (`push`)
|
|
8987
|
+
* and ends it (`close` / `fail`) regardless of consumption, while a consumer reads them back live
|
|
8988
|
+
* through the `drain` async-iterator. Decoupling write from read is what lets a producer make
|
|
8989
|
+
* progress without a consumer pulling, and it is why an agent's `result` settles whether or not
|
|
8990
|
+
* its `events` are drained.
|
|
8991
|
+
*
|
|
8992
|
+
* @remarks
|
|
8993
|
+
* A waiting `drain` call parks its void resolver in the `#waiting` field. The `push` method links a
|
|
8994
|
+
* cell after the `#tail` cell, and the `close` and `fail` methods flag the end; each then fires and
|
|
8995
|
+
* clears every parked resolver, so each parked reader wakes, reads the `#head` cell, and either
|
|
8996
|
+
* takes the next chunk, returns (on `close`), throws (on `fail`), or parks again when another
|
|
8997
|
+
* reader took the chunk first. Taking a chunk reads its value and moves `#head` to the next cell
|
|
8998
|
+
* synchronously, so each value reaches exactly one reader, a push and a take each cost constant
|
|
8999
|
+
* time, and every taken cell but the newest is released at once; the `#tail` field holds the newest
|
|
9000
|
+
* cell until the next push links after it, so a channel retains at most one taken value. A reader
|
|
9001
|
+
* suspended at its `yield` expression holds only the value it took, never a cell, so the bound on
|
|
9002
|
+
* retained values covers both the channel's fields and a suspended reader.
|
|
9003
|
+
* A `return()` issued while a read is parked waits for the next `push`, `close`, or `fail`.
|
|
9004
|
+
* The parked read takes a pushed value before that `return()` settles.
|
|
9005
|
+
*
|
|
9006
|
+
* @example
|
|
9007
|
+
* ```ts
|
|
9008
|
+
* import { Channel } from '@orkestrel/agent'
|
|
9009
|
+
*
|
|
9010
|
+
* const channel = new Channel<number>()
|
|
9011
|
+
* channel.push(1)
|
|
9012
|
+
* channel.close()
|
|
9013
|
+
* for await (const value of channel.drain()) {
|
|
9014
|
+
* value // 1
|
|
9015
|
+
* }
|
|
9016
|
+
* ```
|
|
9017
|
+
*/
|
|
9018
|
+
var Channel = class {
|
|
9019
|
+
#head;
|
|
9020
|
+
#tail;
|
|
9021
|
+
#waiting = [];
|
|
9022
|
+
#closed = false;
|
|
9023
|
+
#failure;
|
|
9024
|
+
push(value) {
|
|
9025
|
+
const cell = new ChannelCell(value);
|
|
9026
|
+
this.#tail?.link(cell);
|
|
9027
|
+
this.#tail = cell;
|
|
9028
|
+
this.#head ??= cell;
|
|
9029
|
+
this.#signal();
|
|
9030
|
+
}
|
|
9031
|
+
close() {
|
|
9032
|
+
this.#closed = true;
|
|
9033
|
+
this.#signal();
|
|
9034
|
+
}
|
|
9035
|
+
fail(error) {
|
|
9036
|
+
if (this.#failure === void 0) this.#failure = { error };
|
|
9037
|
+
this.#closed = true;
|
|
9038
|
+
this.#signal();
|
|
9039
|
+
}
|
|
9040
|
+
async *drain() {
|
|
9041
|
+
for (;;) {
|
|
9042
|
+
while (this.#head !== void 0) {
|
|
9043
|
+
const value = this.#head.value;
|
|
9044
|
+
this.#head = this.#head.next;
|
|
9045
|
+
yield value;
|
|
9046
|
+
}
|
|
9047
|
+
if (this.#failure !== void 0) throw this.#failure.error;
|
|
9048
|
+
if (this.#closed) return;
|
|
9049
|
+
await this.#park();
|
|
9050
|
+
}
|
|
9051
|
+
}
|
|
9052
|
+
#signal() {
|
|
9053
|
+
const waiting = this.#waiting;
|
|
9054
|
+
this.#waiting = [];
|
|
9055
|
+
for (const wake of waiting) wake();
|
|
9056
|
+
}
|
|
9057
|
+
#park() {
|
|
9058
|
+
return new Promise((resolve) => {
|
|
9059
|
+
this.#waiting = [...this.#waiting, resolve];
|
|
9060
|
+
});
|
|
9061
|
+
}
|
|
9062
|
+
};
|
|
7486
9063
|
// src/core/agents/Agent.ts
|
|
7487
9064
|
/**
|
|
7488
9065
|
* Composes a {@link ProviderInterface}, an {@link AgentContext}, and a
|
|
@@ -7547,9 +9124,12 @@ var Agent = class {
|
|
|
7547
9124
|
#window;
|
|
7548
9125
|
#strict;
|
|
7549
9126
|
#emitter;
|
|
9127
|
+
#builtins;
|
|
9128
|
+
#workspace;
|
|
7550
9129
|
#settled = "idle";
|
|
7551
9130
|
#runs = /* @__PURE__ */ new Map();
|
|
7552
9131
|
constructor(provider, options) {
|
|
9132
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, AGENT_OPTION_KEYS, "Agent", "AgentOptions");
|
|
7553
9133
|
if (options?.tools !== void 0 && options.authority === void 0) throw new AgentError("AUTHORITY", "tools are set and no authority is; pass authority: createAuthority({ fallback: { zone: 'default', verdict: 'allow' } }) to allow every call, or rules with a deny fallback");
|
|
7554
9134
|
this.#limit = requireLimit(options?.limit ?? 10);
|
|
7555
9135
|
this.#id = crypto.randomUUID();
|
|
@@ -7566,6 +9146,9 @@ var Agent = class {
|
|
|
7566
9146
|
...options?.conversations === void 0 ? {} : { conversations: options.conversations },
|
|
7567
9147
|
...options?.select === void 0 ? {} : { select: options.select }
|
|
7568
9148
|
});
|
|
9149
|
+
this.#builtins = requireBuiltins(options?.builtins, this.#context.tools);
|
|
9150
|
+
if (this.#builtins.length > 0 && options?.authority === void 0) throw new AgentError("AUTHORITY", "builtins are set and no authority is; pass authority: createAuthority({ fallback: { zone: 'default', verdict: 'allow' } }) to allow every call, or rules with a deny fallback");
|
|
9151
|
+
this.#workspace = requireWorkspace(options?.workspace);
|
|
7569
9152
|
this.#timeoutMs = options?.timeout;
|
|
7570
9153
|
this.#budget = options?.budget;
|
|
7571
9154
|
this.#allowance = options?.allowance;
|
|
@@ -7592,6 +9175,7 @@ var Agent = class {
|
|
|
7592
9175
|
return this.stream(options).result;
|
|
7593
9176
|
}
|
|
7594
9177
|
stream(options) {
|
|
9178
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, AGENT_RUN_KEYS, "Agent", "AgentRunOptions");
|
|
7595
9179
|
const prompt = options?.prompt;
|
|
7596
9180
|
if (prompt !== void 0 && !agentPromptContract.is(prompt)) {
|
|
7597
9181
|
const faults = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.describeFaults)(agentPromptContract.audit(prompt), agentPromptContract.schema));
|
|
@@ -7604,11 +9188,13 @@ var Agent = class {
|
|
|
7604
9188
|
}
|
|
7605
9189
|
});
|
|
7606
9190
|
}
|
|
9191
|
+
const workspace = requireWorkspace(options?.workspace) ?? this.#workspace;
|
|
7607
9192
|
const limit = options?.limit === void 0 ? this.#limit : requireLimit(options.limit);
|
|
7608
9193
|
const budget = requireBudget(options?.budget ?? this.#budget, "budget");
|
|
7609
9194
|
const allowance = requireBudget(options?.allowance ?? this.#allowance, "allowance");
|
|
7610
9195
|
const held = [budget, allowance].filter((one, index, all) => one !== void 0 && all.indexOf(one) === index);
|
|
7611
9196
|
if (this.#runs.size > 0 && (this.#window !== void 0 || [...this.#runs.values()].some((live) => live.budgets.some((one) => held.includes(one))))) throw new AgentError("CONCURRENCY", "a live run on this agent holds the window or this budget or allowance instance; use separate agents or distinct instances per run", { context: { agent: this.#id } });
|
|
9197
|
+
const scope = requireScope(options?.scope);
|
|
7612
9198
|
const conversation = requireConversation(options?.conversation) ?? this.#context.messages;
|
|
7613
9199
|
if ([...this.#runs.values()].some((live) => live.conversation === conversation)) throw new AgentError("CONCURRENCY", "a live run on this agent holds this conversation; await it or bind the run to another conversation", { context: {
|
|
7614
9200
|
agent: this.#id,
|
|
@@ -7632,7 +9218,8 @@ var Agent = class {
|
|
|
7632
9218
|
this.#emitter.emit("start", this.id);
|
|
7633
9219
|
const channel = new Channel();
|
|
7634
9220
|
const settled = Promise.withResolvers();
|
|
7635
|
-
|
|
9221
|
+
const run = new AgentRun(this.#context, conversation, scope, workspace, this.#builtins);
|
|
9222
|
+
this.#drive(abort, run, channel, options?.think, options?.schema, limit, budget, allowance).then((outcome) => this.#finish(abort, timeout, channel, settled, limit, outcome), (error) => this.#fail(abort, timeout, channel, settled, error));
|
|
7636
9223
|
settled.promise.catch(() => {});
|
|
7637
9224
|
return {
|
|
7638
9225
|
events: this.#drainEvents(channel, abort),
|
|
@@ -7662,10 +9249,10 @@ var Agent = class {
|
|
|
7662
9249
|
settled.reject(error);
|
|
7663
9250
|
this.#emitter.emit("error", error);
|
|
7664
9251
|
}
|
|
7665
|
-
async #drive(abort,
|
|
7666
|
-
const
|
|
9252
|
+
async #drive(abort, run, channel, think, schema, limit, budget, allowance) {
|
|
9253
|
+
const steps = this.#execute(abort, run, think, schema, limit, budget, allowance);
|
|
7667
9254
|
for (;;) {
|
|
7668
|
-
const next = await
|
|
9255
|
+
const next = await steps.next();
|
|
7669
9256
|
if (next.done === true) return next.value;
|
|
7670
9257
|
channel.push(next.value);
|
|
7671
9258
|
}
|
|
@@ -7677,15 +9264,16 @@ var Agent = class {
|
|
|
7677
9264
|
abort.abort();
|
|
7678
9265
|
}
|
|
7679
9266
|
}
|
|
7680
|
-
async *#execute(abort,
|
|
9267
|
+
async *#execute(abort, run, think, schema, limit, budget, allowance) {
|
|
9268
|
+
const conversation = run.conversation;
|
|
7681
9269
|
const last = conversation.view().at(-1);
|
|
7682
9270
|
const request = last?.role === "user" ? last : void 0;
|
|
7683
|
-
|
|
9271
|
+
run.observe();
|
|
9272
|
+
const selection = this.#select(run, request, abort, budget);
|
|
7684
9273
|
const entry = selection === void 0 ? {
|
|
7685
|
-
messages: this.#build(
|
|
9274
|
+
messages: this.#build(run),
|
|
7686
9275
|
usage: void 0
|
|
7687
9276
|
} : await selection;
|
|
7688
|
-
const tools = this.#context.tools;
|
|
7689
9277
|
const options = think === void 0 && schema === void 0 ? void 0 : {
|
|
7690
9278
|
...think === void 0 ? {} : { think },
|
|
7691
9279
|
...schema === void 0 ? {} : { schema }
|
|
@@ -7694,14 +9282,14 @@ var Agent = class {
|
|
|
7694
9282
|
const turns = [];
|
|
7695
9283
|
while (turns.length < limit && (turns.at(-1)?.outcome ?? "tools") === "tools") {
|
|
7696
9284
|
const index = turns.length;
|
|
7697
|
-
const
|
|
9285
|
+
const prior = turns.at(-1)?.messages ?? entry.messages;
|
|
7698
9286
|
const futile = turns.at(-1)?.futile ?? false;
|
|
7699
9287
|
const stopped = {
|
|
7700
9288
|
content: "",
|
|
7701
9289
|
thinking: void 0,
|
|
7702
9290
|
usage: void 0,
|
|
7703
9291
|
spent: void 0,
|
|
7704
|
-
messages:
|
|
9292
|
+
messages: prior,
|
|
7705
9293
|
outcome: "cancel",
|
|
7706
9294
|
partial: true,
|
|
7707
9295
|
futile,
|
|
@@ -7720,9 +9308,10 @@ var Agent = class {
|
|
|
7720
9308
|
turns.push(stopped);
|
|
7721
9309
|
continue;
|
|
7722
9310
|
}
|
|
7723
|
-
const
|
|
7724
|
-
const
|
|
7725
|
-
const
|
|
9311
|
+
const base = run.observe() ? this.#refresh(run, prior) : prior;
|
|
9312
|
+
const names = run.ceiling.tools;
|
|
9313
|
+
const advertised = run.advertised;
|
|
9314
|
+
const held = new Map(advertised.map((definition) => [definition.name, run.tool(definition.name)]));
|
|
7726
9315
|
const definitions = advertised.length > 0 ? advertised : void 0;
|
|
7727
9316
|
const composed = this.#compose(base, definitions, options);
|
|
7728
9317
|
const admitted = this.#window === void 0 ? {
|
|
@@ -7730,7 +9319,7 @@ var Agent = class {
|
|
|
7730
9319
|
messages: base,
|
|
7731
9320
|
spent: void 0,
|
|
7732
9321
|
futile
|
|
7733
|
-
} : await this.#admit(this.#window,
|
|
9322
|
+
} : await this.#admit(this.#window, run, composed, base, request, index > 0, futile, abort, budget);
|
|
7734
9323
|
if (admitted.dispatch === void 0) {
|
|
7735
9324
|
turns.push({
|
|
7736
9325
|
...stopped,
|
|
@@ -7740,12 +9329,25 @@ var Agent = class {
|
|
|
7740
9329
|
});
|
|
7741
9330
|
continue;
|
|
7742
9331
|
}
|
|
9332
|
+
const dispatch = admitted.dispatch;
|
|
9333
|
+
Object.freeze(dispatch.messages);
|
|
9334
|
+
if (dispatch.tools !== void 0) Object.freeze(dispatch.tools);
|
|
9335
|
+
this.#emitter.emit("request", index, Object.freeze(dispatch), conversation);
|
|
9336
|
+
if (abort.signal.aborted) {
|
|
9337
|
+
turns.push({
|
|
9338
|
+
...stopped,
|
|
9339
|
+
messages: admitted.messages,
|
|
9340
|
+
spent: admitted.spent,
|
|
9341
|
+
futile: admitted.futile
|
|
9342
|
+
});
|
|
9343
|
+
continue;
|
|
9344
|
+
}
|
|
7743
9345
|
const streamed = [];
|
|
7744
9346
|
const meter = allowance === void 0 ? void 0 : (0, _orkestrel_budget.createTokenMeter)({
|
|
7745
9347
|
budget: allowance,
|
|
7746
9348
|
estimator: estimateTokens
|
|
7747
9349
|
});
|
|
7748
|
-
const provided = yield* this.#provide(
|
|
9350
|
+
const provided = yield* this.#provide(dispatch, abort.signal, streamed, meter);
|
|
7749
9351
|
const settled = this.#reconcile(provided, abort, streamed, admitted, meter, budget, allowance);
|
|
7750
9352
|
if (!provided.success) {
|
|
7751
9353
|
turns.push({
|
|
@@ -7768,8 +9370,8 @@ var Agent = class {
|
|
|
7768
9370
|
if (result.tools !== void 0 && result.tools.length > 0) {
|
|
7769
9371
|
const admittedCalls = filterAllowList(names, result.tools, (call) => call.name);
|
|
7770
9372
|
const authorization = this.#authorize(result.tools, admittedCalls, advertised, held);
|
|
7771
|
-
if (authorization.asked.length > 0 && !abort.signal.aborted) yield* this.#approve(
|
|
7772
|
-
if (!abort.signal.aborted) this.#confirm(
|
|
9373
|
+
if (authorization.asked.length > 0 && !abort.signal.aborted) yield* this.#approve(run, authorization, abort.signal);
|
|
9374
|
+
if (!abort.signal.aborted) this.#confirm(run, authorization, advertised);
|
|
7773
9375
|
if (abort.signal.aborted) {
|
|
7774
9376
|
turns.push({
|
|
7775
9377
|
...settled,
|
|
@@ -7784,7 +9386,7 @@ var Agent = class {
|
|
|
7784
9386
|
calls: result.tools,
|
|
7785
9387
|
...recorded
|
|
7786
9388
|
});
|
|
7787
|
-
const results = await this.#dispatch(
|
|
9389
|
+
const results = await this.#dispatch(run, result.tools, abort.signal, authorization, advertised);
|
|
7788
9390
|
const replies = [];
|
|
7789
9391
|
for (const [position, call] of result.tools.entries()) {
|
|
7790
9392
|
const dispatched = results[position];
|
|
@@ -7880,11 +9482,11 @@ var Agent = class {
|
|
|
7880
9482
|
#compose(messages, tools, options) {
|
|
7881
9483
|
return {
|
|
7882
9484
|
messages: [...stripThinking(messages, this.#replay)],
|
|
7883
|
-
...tools === void 0 ? {} : { tools },
|
|
9485
|
+
...tools === void 0 ? {} : { tools: [...tools] },
|
|
7884
9486
|
...options === void 0 ? {} : { options }
|
|
7885
9487
|
};
|
|
7886
9488
|
}
|
|
7887
|
-
async #admit(window,
|
|
9489
|
+
async #admit(window, run, composed, messages, request, latch, futile, abort, budget) {
|
|
7888
9490
|
window.clear();
|
|
7889
9491
|
window.consume(composed);
|
|
7890
9492
|
if (!window.exhausted) return {
|
|
@@ -7899,15 +9501,16 @@ var Agent = class {
|
|
|
7899
9501
|
spent: void 0,
|
|
7900
9502
|
futile
|
|
7901
9503
|
};
|
|
9504
|
+
const conversation = run.conversation;
|
|
7902
9505
|
if (!conversation.summarizable || futile) return {
|
|
7903
9506
|
dispatch: this.#enforce(window, composed),
|
|
7904
9507
|
messages,
|
|
7905
9508
|
spent: void 0,
|
|
7906
9509
|
futile
|
|
7907
9510
|
};
|
|
7908
|
-
return conversation.compact({ signal: abort.signal }).then((section) => this.#refold(section, window,
|
|
9511
|
+
return conversation.compact({ signal: abort.signal }).then((section) => this.#refold(section, window, run, composed, messages, request, latch, abort, budget), (error) => this.#unfold(error, composed, messages, abort));
|
|
7909
9512
|
}
|
|
7910
|
-
async #refold(section, window,
|
|
9513
|
+
async #refold(section, window, run, composed, messages, request, latch, abort, budget) {
|
|
7911
9514
|
if (abort.signal.aborted) return {
|
|
7912
9515
|
dispatch: void 0,
|
|
7913
9516
|
messages,
|
|
@@ -7920,9 +9523,9 @@ var Agent = class {
|
|
|
7920
9523
|
spent: void 0,
|
|
7921
9524
|
futile: latch
|
|
7922
9525
|
};
|
|
7923
|
-
const selection = this.#select(
|
|
9526
|
+
const selection = this.#select(run, request, abort, budget);
|
|
7924
9527
|
const rebuilt = selection === void 0 ? {
|
|
7925
|
-
messages: this.#build(
|
|
9528
|
+
messages: this.#build(run),
|
|
7926
9529
|
usage: void 0
|
|
7927
9530
|
} : await selection;
|
|
7928
9531
|
if (abort.signal.aborted) return {
|
|
@@ -7971,47 +9574,61 @@ var Agent = class {
|
|
|
7971
9574
|
} });
|
|
7972
9575
|
return request;
|
|
7973
9576
|
}
|
|
7974
|
-
#select(
|
|
7975
|
-
const pending = request === void 0 ? void 0 : this.#context.select(conversation, request, abort.signal);
|
|
7976
|
-
return pending === void 0 ? void 0 : pending.then((selection) => this.#adopt(
|
|
9577
|
+
#select(run, request, abort, budget) {
|
|
9578
|
+
const pending = request === void 0 ? void 0 : this.#context.select(run.conversation, request, abort.signal, run.scope);
|
|
9579
|
+
return pending === void 0 ? void 0 : pending.then((selection) => this.#adopt(run, selection, abort, budget), (error) => this.#reject(run, error, abort));
|
|
7977
9580
|
}
|
|
7978
|
-
#adopt(
|
|
9581
|
+
#adopt(run, selection, abort, budget) {
|
|
7979
9582
|
const usage = selection.usage === void 0 ? void 0 : sanitizeUsage(selection.usage);
|
|
7980
9583
|
if (usage !== void 0) chargeUsage(budget, usage, 0);
|
|
7981
9584
|
if (abort.signal.aborted) return {
|
|
7982
|
-
messages: this.#build(
|
|
9585
|
+
messages: this.#build(run),
|
|
7983
9586
|
usage
|
|
7984
9587
|
};
|
|
7985
9588
|
if (selection.fault !== void 0) {
|
|
7986
9589
|
this.#emitter.emit("fault", selection.fault);
|
|
7987
9590
|
if (abort.signal.aborted) return {
|
|
7988
|
-
messages: this.#build(
|
|
9591
|
+
messages: this.#build(run),
|
|
7989
9592
|
usage
|
|
7990
9593
|
};
|
|
7991
9594
|
if (this.#strict) throw selection.fault;
|
|
7992
9595
|
}
|
|
7993
|
-
const messages = this.#build(
|
|
9596
|
+
const messages = this.#build(run, selection);
|
|
7994
9597
|
this.#emitter.emit("select", selection);
|
|
7995
9598
|
return {
|
|
7996
9599
|
messages,
|
|
7997
9600
|
usage
|
|
7998
9601
|
};
|
|
7999
9602
|
}
|
|
8000
|
-
#reject(
|
|
9603
|
+
#reject(run, error, abort) {
|
|
8001
9604
|
if (!abort.signal.aborted) {
|
|
8002
9605
|
this.#emitter.emit("fault", error);
|
|
8003
9606
|
if (this.#strict && !abort.signal.aborted) throw error;
|
|
8004
9607
|
}
|
|
8005
9608
|
return {
|
|
8006
|
-
messages: this.#build(
|
|
9609
|
+
messages: this.#build(run),
|
|
8007
9610
|
usage: void 0
|
|
8008
9611
|
};
|
|
8009
9612
|
}
|
|
8010
|
-
#build(
|
|
8011
|
-
|
|
8012
|
-
messages: conversation.view(),
|
|
9613
|
+
#build(run, selection) {
|
|
9614
|
+
const rendered = selection ?? {
|
|
9615
|
+
messages: run.conversation.view(),
|
|
8013
9616
|
judgments: []
|
|
9617
|
+
};
|
|
9618
|
+
const scope = run.snapshot;
|
|
9619
|
+
const disclosure = run.disclosure;
|
|
9620
|
+
const messages = this.#context.build({
|
|
9621
|
+
selection: rendered,
|
|
9622
|
+
...scope === void 0 ? {} : { scope },
|
|
9623
|
+
...disclosure === void 0 ? {} : { disclosure },
|
|
9624
|
+
...run.switchable ? { switchable: true } : {}
|
|
8014
9625
|
});
|
|
9626
|
+
run.record(rendered, messages.length);
|
|
9627
|
+
return messages;
|
|
9628
|
+
}
|
|
9629
|
+
#refresh(run, messages) {
|
|
9630
|
+
const added = messages.slice(run.length);
|
|
9631
|
+
return [...this.#build(run, run.selection), ...added];
|
|
8015
9632
|
}
|
|
8016
9633
|
#authorize(calls, admitted, advertised, held) {
|
|
8017
9634
|
const allowed = /* @__PURE__ */ new Map();
|
|
@@ -8084,7 +9701,7 @@ var Agent = class {
|
|
|
8084
9701
|
}
|
|
8085
9702
|
return (0, _orkestrel_contract.success)(evaluated.value);
|
|
8086
9703
|
}
|
|
8087
|
-
async *#approve(
|
|
9704
|
+
async *#approve(run, authorization, signal) {
|
|
8088
9705
|
const authority = this.#authority;
|
|
8089
9706
|
for (const { index, call, decision, tool } of authorization.asked) {
|
|
8090
9707
|
if (signal.aborted) return;
|
|
@@ -8113,7 +9730,7 @@ var Agent = class {
|
|
|
8113
9730
|
};
|
|
8114
9731
|
const outcome = await raceSignal(answer, signal).catch(() => void 0);
|
|
8115
9732
|
if (outcome === void 0 || signal.aborted) return;
|
|
8116
|
-
if (outcome.approved &&
|
|
9733
|
+
if (outcome.approved && run.tool(call.name) === tool) {
|
|
8117
9734
|
authorization.allowed.set(index, {
|
|
8118
9735
|
call,
|
|
8119
9736
|
tool
|
|
@@ -8125,29 +9742,32 @@ var Agent = class {
|
|
|
8125
9742
|
}
|
|
8126
9743
|
}
|
|
8127
9744
|
}
|
|
8128
|
-
#changed(
|
|
9745
|
+
#changed(run, call, tool, advertised) {
|
|
8129
9746
|
const same = (0, _orkestrel_contract.attempt)(() => {
|
|
8130
|
-
const current =
|
|
9747
|
+
const current = run.tool(call.name);
|
|
8131
9748
|
const definition = advertised.find((entry) => entry.name === call.name);
|
|
8132
9749
|
return current === tool && current?.annotations?.consequential === true === (definition?.annotations?.consequential === true);
|
|
8133
9750
|
});
|
|
8134
9751
|
return !same.success || !same.value;
|
|
8135
9752
|
}
|
|
8136
|
-
#confirm(
|
|
9753
|
+
#confirm(run, authorization, advertised) {
|
|
8137
9754
|
for (const [index, { call, tool }] of authorization.allowed) {
|
|
8138
|
-
if (!this.#changed(
|
|
9755
|
+
if (!this.#changed(run, call, tool, advertised)) continue;
|
|
8139
9756
|
authorization.allowed.delete(index);
|
|
8140
9757
|
authorization.denials.set(index, denyCall(call, AUTHORITY_NOTES.denied));
|
|
8141
9758
|
this.#emitter.emit("deny", call, `${call.name} changed after its decision`, void 0);
|
|
8142
|
-
this.#confirm(
|
|
9759
|
+
this.#confirm(run, authorization, advertised);
|
|
8143
9760
|
return;
|
|
8144
9761
|
}
|
|
8145
9762
|
}
|
|
8146
|
-
async #dispatch(
|
|
9763
|
+
async #dispatch(run, calls, signal, authorization, advertised) {
|
|
8147
9764
|
const { allowed, denials } = authorization;
|
|
8148
9765
|
const entries = [...allowed].sort(([left], [right]) => left - right);
|
|
8149
9766
|
const started = entries.map(([, { call, tool }]) => {
|
|
8150
|
-
if (!this.#changed(
|
|
9767
|
+
if (!this.#changed(run, call, tool, advertised)) return run.execute(call, {
|
|
9768
|
+
signal,
|
|
9769
|
+
caller: run.caller
|
|
9770
|
+
});
|
|
8151
9771
|
this.#emitter.emit("deny", call, `${call.name} changed after its decision`, void 0);
|
|
8152
9772
|
return denyCall(call, AUTHORITY_NOTES.denied);
|
|
8153
9773
|
});
|
|
@@ -8216,6 +9836,17 @@ var Agent = class {
|
|
|
8216
9836
|
* awaited inline, so a bounded runner frees the parent's slot for them. `createAgentRunner`
|
|
8217
9837
|
* creates one instance per runner and binds its `execute` method as the runner's handler.
|
|
8218
9838
|
*
|
|
9839
|
+
* A child job runs within its parent. Before a unit spawns or runs, on every attempt, the
|
|
9840
|
+
* `execute` method walks the whole tree under it through the `scanJobTree` helper and refuses the
|
|
9841
|
+
* unit when any job exceeds its own parent on `tools`, `builtins`, `scope`, `authority`,
|
|
9842
|
+
* `provider`, `scheduler`, `limit`, `timeout`, or `budget`, or when a job is malformed or its build
|
|
9843
|
+
* would refuse it (the lines `collectJobRefusals` returns), so nothing in a refused tree spawns, builds,
|
|
9844
|
+
* or runs. A child's `workspace` is free, because it renders nothing its own `scope`
|
|
9845
|
+
* omits. Each child is held within its parent alone, so the children of one parent can together
|
|
9846
|
+
* spend more than it. A job that needs another provider, another authority, a skill, memo,
|
|
9847
|
+
* workspace, or built-in its parent lacks, or more room than its parent is a root job the host
|
|
9848
|
+
* enqueues.
|
|
9849
|
+
*
|
|
8219
9850
|
* @example
|
|
8220
9851
|
* ```ts
|
|
8221
9852
|
* import type { AgentJobInput, ProviderInterface } from '@orkestrel/agent'
|
|
@@ -8245,14 +9876,32 @@ var AgentFanout = class {
|
|
|
8245
9876
|
this.#partial = partial;
|
|
8246
9877
|
}
|
|
8247
9878
|
/**
|
|
8248
|
-
* Executes one unit:
|
|
8249
|
-
*
|
|
9879
|
+
* Executes one unit: refuses its tree when a job in it is malformed or exceeds its parent, spawns
|
|
9880
|
+
* its declared children unless an earlier attempt of the unit already did, then runs the
|
|
9881
|
+
* rehydrated agent and settles it under the partial policy.
|
|
9882
|
+
*
|
|
9883
|
+
* @remarks
|
|
9884
|
+
* The check runs on every attempt, before the unit spawns or runs, and throws synchronously. The
|
|
9885
|
+
* error's message opens with the rule each kind of finding breaks, a refused job's rule when a
|
|
9886
|
+
* finding opens with `JOB_REFUSAL_PREFIX` and the within-parent rule when a finding names an
|
|
9887
|
+
* exceeded member, then lists every finding, one a line. Its `context.findings` holds the same
|
|
9888
|
+
* findings in walk order.
|
|
8250
9889
|
*
|
|
8251
9890
|
* @param controller - The runner controller for this unit's attempt
|
|
8252
9891
|
* @returns The settled agent result of the unit
|
|
9892
|
+
* @throws {AgentError} Thrown with code `'REGISTRY'` when a job in the unit's tree is malformed or exceeds its parent, or when the registry cannot rehydrate the unit
|
|
8253
9893
|
* @throws {AgentJobError} Thrown when the run ended partial and the partial policy is `false`
|
|
8254
9894
|
*/
|
|
8255
9895
|
execute(controller) {
|
|
9896
|
+
const findings = scanJobTree(controller.input);
|
|
9897
|
+
if (findings.length > 0) {
|
|
9898
|
+
const refused = findings.filter((finding) => finding.startsWith(JOB_REFUSAL_PREFIX));
|
|
9899
|
+
throw new AgentError("REGISTRY", [
|
|
9900
|
+
...refused.length === 0 ? [] : ["every job in a tree must be readable, hold only the declared members of AgentJobInput, and build under its parent; correct each job a following finding names"],
|
|
9901
|
+
...refused.length === findings.length ? [] : ["a child job must stay within its parent on tools, builtins, scope, authority, provider, scheduler, limit, timeout, and budget; enqueue a job that needs more as a root job"],
|
|
9902
|
+
...findings
|
|
9903
|
+
].join("\n"), { context: { findings } });
|
|
9904
|
+
}
|
|
8256
9905
|
const children = controller.input.children;
|
|
8257
9906
|
if (children !== void 0 && !this.#spawned.has(controller.id)) {
|
|
8258
9907
|
this.#spawned = /* @__PURE__ */ new Set([...this.#spawned, controller.id]);
|
|
@@ -8306,6 +9955,7 @@ var Authority = class {
|
|
|
8306
9955
|
#handler;
|
|
8307
9956
|
constructor(options) {
|
|
8308
9957
|
this.#fallback = requireFallback(options);
|
|
9958
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, AUTHORITY_OPTION_KEYS, "Authority", "AuthorityOptions");
|
|
8309
9959
|
this.#rules = requireRules(options.rules);
|
|
8310
9960
|
this.#handler = options.ask;
|
|
8311
9961
|
}
|
|
@@ -8428,16 +10078,22 @@ var Approver = class {
|
|
|
8428
10078
|
* the message `unknown <category>: <name>` when it is absent — an unknown name in a
|
|
8429
10079
|
* rehydrated job is a programmer / config error that must fail loudly at build time, never
|
|
8430
10080
|
* silently resolve to `undefined` and run an agent missing a dependency.
|
|
8431
|
-
* - **`build` rehydrates.**
|
|
8432
|
-
*
|
|
8433
|
-
*
|
|
8434
|
-
*
|
|
10081
|
+
* - **`build` rehydrates.** Refuse with `code: 'REGISTRY'` a job `agentJobContract` refuses,
|
|
10082
|
+
* its faults under `context.faults`, and a job `collectJobRefusals` refuses, its lines under
|
|
10083
|
+
* `context.refusals`: tools or built-ins with no `authority`, a `builtins` list an agent
|
|
10084
|
+
* refuses, and a built-in the job's `tools` also name. Then resolve the job's `provider`;
|
|
10085
|
+
* assemble a fresh {@link ToolManager} from the `tools` names; rebuild the token `budget` from
|
|
10086
|
+
* its ceiling (`createTokenBudget({ max })`); resolve the optional `authority` /
|
|
8435
10087
|
* `scheduler` names; construct the {@link Agent} with `system` / `limit` / `timeout` /
|
|
8436
10088
|
* the threaded `signal`; seed its context with the job's `messages`; return it. The
|
|
8437
10089
|
* `signal` is the queue attempt's / runner unit's cancel, so a bounded abort propagates
|
|
8438
10090
|
* into the agent (which commits a partial — the job's `partial` policy then
|
|
8439
|
-
* decides success vs. retry). A job
|
|
8440
|
-
*
|
|
10091
|
+
* decides success vs. retry). A job is a record, so a malformed `limit`, `timeout`, `budget`,
|
|
10092
|
+
* seed role, or `builtins` list refuses as one, with `code: 'REGISTRY'`.
|
|
10093
|
+
* - **A job holds what it names.** A job with a `scope` gets the registry's `skills`, `memos`, and
|
|
10094
|
+
* `workspaces` managers, which every scoped job shares, and a scope named `job` built from its
|
|
10095
|
+
* three lists; a job with no `scope` gets none of them. Its `workspace` and `builtins` pass to
|
|
10096
|
+
* the agent.
|
|
8441
10097
|
* - **Event-free.** A pure resolver — no Emitter, no events.
|
|
8442
10098
|
*
|
|
8443
10099
|
* @example
|
|
@@ -8454,12 +10110,19 @@ var AgentRegistry = class {
|
|
|
8454
10110
|
#authorities;
|
|
8455
10111
|
#schedulers;
|
|
8456
10112
|
#store;
|
|
10113
|
+
#skills;
|
|
10114
|
+
#memos;
|
|
10115
|
+
#workspaces;
|
|
8457
10116
|
constructor(options) {
|
|
10117
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, REGISTRY_OPTION_KEYS, "AgentRegistry", "AgentRegistryOptions");
|
|
8458
10118
|
this.#providers = new Map(Object.entries(options.providers));
|
|
8459
10119
|
this.#tools = new Map(Object.entries(options.tools ?? {}));
|
|
8460
10120
|
this.#authorities = new Map(Object.entries(options.authorities ?? {}));
|
|
8461
10121
|
this.#schedulers = new Map(Object.entries(options.schedulers ?? {}));
|
|
8462
10122
|
this.#store = options.store;
|
|
10123
|
+
this.#skills = options.skills;
|
|
10124
|
+
this.#memos = options.memos;
|
|
10125
|
+
this.#workspaces = options.workspaces;
|
|
8463
10126
|
}
|
|
8464
10127
|
provider(name) {
|
|
8465
10128
|
return requireEntry(this.#providers, "provider", name);
|
|
@@ -8476,13 +10139,16 @@ var AgentRegistry = class {
|
|
|
8476
10139
|
build(input, signal) {
|
|
8477
10140
|
const faults = agentJobContract.audit(input);
|
|
8478
10141
|
if (faults.length > 0) throw new AgentError("REGISTRY", `a job must hold only the declared members of AgentJobInput\n${(0, _orkestrel_contract.describeFaults)(faults, agentJobContract.schema)}`, { context: { faults } });
|
|
10142
|
+
const refusals = collectJobRefusals(input);
|
|
10143
|
+
if (refusals.length > 0) throw new AgentError("REGISTRY", ["a job must build as it is written; correct each member a following line names", ...refusals].join("\n"), { context: { refusals } });
|
|
8479
10144
|
const agent = new Agent(this.provider(input.provider), this.#buildOptions(input, signal));
|
|
8480
10145
|
for (const message of input.messages ?? []) agent.context.messages.add(message);
|
|
8481
10146
|
return agent;
|
|
8482
10147
|
}
|
|
8483
10148
|
#buildOptions(input, signal) {
|
|
8484
10149
|
const names = input.tools ?? [];
|
|
8485
|
-
|
|
10150
|
+
const builtins = input.builtins ?? [];
|
|
10151
|
+
const scope = input.scope;
|
|
8486
10152
|
const budget = input.budget === void 0 ? void 0 : (0, _orkestrel_budget.createTokenBudget)({ max: input.budget });
|
|
8487
10153
|
return {
|
|
8488
10154
|
...names.length === 0 ? {} : { tools: this.#buildToolManager(names) },
|
|
@@ -8493,6 +10159,15 @@ var AgentRegistry = class {
|
|
|
8493
10159
|
...input.authority === void 0 ? {} : { authority: this.authority(input.authority) },
|
|
8494
10160
|
...input.scheduler === void 0 ? {} : { scheduler: this.scheduler(input.scheduler) },
|
|
8495
10161
|
...this.#store === void 0 ? {} : { conversations: new ConversationManager({ store: this.#store }) },
|
|
10162
|
+
...scope === void 0 || this.#skills === void 0 ? {} : { skills: this.#skills },
|
|
10163
|
+
...scope === void 0 || this.#memos === void 0 ? {} : { memos: this.#memos },
|
|
10164
|
+
...scope === void 0 || this.#workspaces === void 0 ? {} : { workspaces: this.#workspaces },
|
|
10165
|
+
...scope === void 0 ? {} : { scope: createScope({
|
|
10166
|
+
name: "job",
|
|
10167
|
+
...scope
|
|
10168
|
+
}) },
|
|
10169
|
+
...input.workspace === void 0 ? {} : { workspace: input.workspace },
|
|
10170
|
+
...builtins.length === 0 ? {} : { builtins },
|
|
8496
10171
|
...signal === void 0 ? {} : { signal }
|
|
8497
10172
|
};
|
|
8498
10173
|
}
|
|
@@ -8727,6 +10402,7 @@ function createAgentRegistry(options) {
|
|
|
8727
10402
|
* ```
|
|
8728
10403
|
*/
|
|
8729
10404
|
function createAgentQueue(options) {
|
|
10405
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, QUEUE_OPTION_KEYS, "createAgentQueue", "AgentQueueOptions");
|
|
8730
10406
|
const { registry, partial = false, store } = options;
|
|
8731
10407
|
return (0, _orkestrel_queue.createQueue)({
|
|
8732
10408
|
...extractQueueOptions(options),
|
|
@@ -8773,6 +10449,7 @@ function createAgentQueue(options) {
|
|
|
8773
10449
|
* ```
|
|
8774
10450
|
*/
|
|
8775
10451
|
function createAgentRunner(options) {
|
|
10452
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, RUNNER_OPTION_KEYS, "createAgentRunner", "AgentRunnerOptions");
|
|
8776
10453
|
const { registry, partial = false } = options;
|
|
8777
10454
|
const fanout = new AgentFanout(registry, partial);
|
|
8778
10455
|
return (0, _orkestrel_workflow.createRunner)({
|
|
@@ -8780,6 +10457,38 @@ function createAgentRunner(options) {
|
|
|
8780
10457
|
handler: fanout.execute.bind(fanout)
|
|
8781
10458
|
});
|
|
8782
10459
|
}
|
|
10460
|
+
/**
|
|
10461
|
+
* Creates the tool a run serves for one built-in: its description and annotations from
|
|
10462
|
+
* `BUILTIN_TOOLS`, its argument shape from `builtinToolContracts`, and the given handler.
|
|
10463
|
+
*
|
|
10464
|
+
* @remarks
|
|
10465
|
+
* The tool's door refuses a missing, empty, over-long, or extra argument before the handler runs,
|
|
10466
|
+
* and repairs a value that converts to a string without loss, such as a number, so the handler
|
|
10467
|
+
* reads only arguments the shape admits. Its definition equals the one `collectBuiltinDefinitions`
|
|
10468
|
+
* returns for the same name.
|
|
10469
|
+
*
|
|
10470
|
+
* @param name - The built-in to create
|
|
10471
|
+
* @param execute - The handler the door hands admitted arguments to
|
|
10472
|
+
* @returns The tool
|
|
10473
|
+
*
|
|
10474
|
+
* @example
|
|
10475
|
+
* ```ts
|
|
10476
|
+
* import { createBuiltinTool } from '@orkestrel/agent'
|
|
10477
|
+
*
|
|
10478
|
+
* const memo = createBuiltinTool('memo', (args) => `read ${String(args.name)}`)
|
|
10479
|
+
* memo.annotations // { pure: true, untrusted: true }
|
|
10480
|
+
* ```
|
|
10481
|
+
*/
|
|
10482
|
+
function createBuiltinTool(name, execute) {
|
|
10483
|
+
const declared = BUILTIN_TOOLS[name];
|
|
10484
|
+
return (0, _orkestrel_tool.createTool)({
|
|
10485
|
+
name,
|
|
10486
|
+
description: declared.description,
|
|
10487
|
+
...declared.annotations === void 0 ? {} : { annotations: declared.annotations },
|
|
10488
|
+
contract: builtinToolContracts[name].shape,
|
|
10489
|
+
execute
|
|
10490
|
+
});
|
|
10491
|
+
}
|
|
8783
10492
|
// src/core/ledgers/constants.ts
|
|
8784
10493
|
/**
|
|
8785
10494
|
* Lists the categories the ledger files a message under, in the order the category question
|
|
@@ -8854,19 +10563,25 @@ var LEDGER_QUESTIONS = Object.freeze({
|
|
|
8854
10563
|
/**
|
|
8855
10564
|
* Supplies the text of each ledger note, worded for a model that answers in its final
|
|
8856
10565
|
* message.
|
|
10566
|
+
*
|
|
10567
|
+
* @remarks
|
|
10568
|
+
* `overflow` names `unload` because a skill the thread shows is the room a model can give back: a
|
|
10569
|
+
* successful `unload` reopens every call the room denied in that request.
|
|
8857
10570
|
*/
|
|
8858
10571
|
var LEDGER_NOTES = Object.freeze({
|
|
8859
10572
|
cue: "Give your complete answer to the request now, from what you already have.",
|
|
8860
10573
|
results: "Your lookups and recalls in this request returned:",
|
|
8861
10574
|
repeat: "You already have this result earlier in this request; give your complete answer now as your final message.",
|
|
8862
|
-
closure: "recall is closed for the rest of this request; give your final answer from what you have"
|
|
10575
|
+
closure: "recall is closed for the rest of this request; give your final answer from what you have",
|
|
10576
|
+
overflow: "there is no room left for it in this request. Unload a skill you no longer need before you call it again, or answer with what you have."
|
|
8863
10577
|
});
|
|
8864
10578
|
/**
|
|
8865
|
-
* Supplies the default prompt and
|
|
10579
|
+
* Supplies the default prompt, tail, and skills shares.
|
|
8866
10580
|
*/
|
|
8867
10581
|
var DEFAULT_LEDGER_SHARE = Object.freeze({
|
|
8868
10582
|
prompt: .7,
|
|
8869
|
-
tail: .35
|
|
10583
|
+
tail: .35,
|
|
10584
|
+
skills: .5
|
|
8870
10585
|
});
|
|
8871
10586
|
/**
|
|
8872
10587
|
* Caps the tool-iteration turns of a ledger's agent at 8 turns.
|
|
@@ -8903,6 +10618,85 @@ var LEDGER_SCALE_DRIFT = .06;
|
|
|
8903
10618
|
var LEDGER_WORD = /[\p{L}\p{N}]+/gu;
|
|
8904
10619
|
/** Matches the logprob decoding failure `invalid or duplicate top logprob token`, which the ledger holds for the ledger's life. */
|
|
8905
10620
|
var DETERMINISTIC_JUDGE_ERROR = /invalid or duplicate top logprob token/;
|
|
10621
|
+
/**
|
|
10622
|
+
* Lists the members `LedgerOptions` declares, frozen — the keys the `Ledger` constructor admits.
|
|
10623
|
+
*
|
|
10624
|
+
* @remarks
|
|
10625
|
+
* The constructor passes the list to `refuseOptionKeys` of `@orkestrel/contract`, so an own
|
|
10626
|
+
* enumerable member outside it refuses with a `ContractError` coded `'structure'`.
|
|
10627
|
+
*/
|
|
10628
|
+
var LEDGER_OPTION_KEYS = Object.freeze([
|
|
10629
|
+
"judge",
|
|
10630
|
+
"system",
|
|
10631
|
+
"topics",
|
|
10632
|
+
"questions",
|
|
10633
|
+
"thresholds",
|
|
10634
|
+
"capacity",
|
|
10635
|
+
"predict",
|
|
10636
|
+
"think",
|
|
10637
|
+
"gauge",
|
|
10638
|
+
"lookups",
|
|
10639
|
+
"writes",
|
|
10640
|
+
"share",
|
|
10641
|
+
"recall",
|
|
10642
|
+
"correction",
|
|
10643
|
+
"agent",
|
|
10644
|
+
"snapshot"
|
|
10645
|
+
]);
|
|
10646
|
+
/**
|
|
10647
|
+
* Lists the members the `LedgerAgentOptions` pick declares, frozen — the keys a ledger's `agent`
|
|
10648
|
+
* option admits.
|
|
10649
|
+
*
|
|
10650
|
+
* @remarks
|
|
10651
|
+
* The `Ledger` constructor passes the list to `refuseOptionKeys` of `@orkestrel/contract`, so an
|
|
10652
|
+
* own enumerable member outside it, an `AgentOptions` member the pick leaves out included, refuses
|
|
10653
|
+
* with a `ContractError` coded `'structure'`.
|
|
10654
|
+
*/
|
|
10655
|
+
var LEDGER_AGENT_KEYS = Object.freeze([
|
|
10656
|
+
"limit",
|
|
10657
|
+
"timeout",
|
|
10658
|
+
"on",
|
|
10659
|
+
"scheduler",
|
|
10660
|
+
"instructions",
|
|
10661
|
+
"skills",
|
|
10662
|
+
"memos",
|
|
10663
|
+
"workspaces",
|
|
10664
|
+
"strict",
|
|
10665
|
+
"authority",
|
|
10666
|
+
"builtins",
|
|
10667
|
+
"workspace"
|
|
10668
|
+
]);
|
|
10669
|
+
/** Lists the members `ClassifierOptions` declares, frozen — the keys the `Classifier` constructor admits. */
|
|
10670
|
+
var CLASSIFIER_OPTION_KEYS = Object.freeze([
|
|
10671
|
+
"strict",
|
|
10672
|
+
"conversation",
|
|
10673
|
+
"judge",
|
|
10674
|
+
"questions",
|
|
10675
|
+
"topics",
|
|
10676
|
+
"thresholds",
|
|
10677
|
+
"assign",
|
|
10678
|
+
"entities",
|
|
10679
|
+
"correction",
|
|
10680
|
+
"scheduler"
|
|
10681
|
+
]);
|
|
10682
|
+
/** Lists the members `GaugeOptions` declares, frozen — the keys the `Gauge` constructor admits. */
|
|
10683
|
+
var GAUGE_OPTION_KEYS = Object.freeze([
|
|
10684
|
+
"scale",
|
|
10685
|
+
"overhead",
|
|
10686
|
+
"capacity",
|
|
10687
|
+
"predict",
|
|
10688
|
+
"replay"
|
|
10689
|
+
]);
|
|
10690
|
+
/**
|
|
10691
|
+
* Sets the 3 UTF-8 bytes one UTF-16 code unit encodes to at most, the divisor that sizes the
|
|
10692
|
+
* workspace text a calibration's third call carries.
|
|
10693
|
+
*
|
|
10694
|
+
* @remarks
|
|
10695
|
+
* A tokenizer that falls back to bytes charges at most one token a byte, so workspace text cut to the
|
|
10696
|
+
* capacity the call without definitions left, divided by this figure, in characters, reports at most
|
|
10697
|
+
* `capacity` apart from a template's few tokens for an added message.
|
|
10698
|
+
*/
|
|
10699
|
+
var LEDGER_SAMPLE_BYTES = 3;
|
|
8906
10700
|
// src/core/ledgers/errors.ts
|
|
8907
10701
|
/**
|
|
8908
10702
|
* Reports an invalid ledger configuration, failed calibration, or unowned request selection,
|
|
@@ -8911,7 +10705,9 @@ var DETERMINISTIC_JUDGE_ERROR = /invalid or duplicate top logprob token/;
|
|
|
8911
10705
|
* @remarks
|
|
8912
10706
|
* A threshold, share, capacity, limit, topic, lookup, write, question, or gauge outside its bounds is a
|
|
8913
10707
|
* programmer error and throws this with the matching {@link LedgerErrorCode}. A calibration call that reports
|
|
8914
|
-
* no prompt usage
|
|
10708
|
+
* no prompt usage, or a calibration call, with the tool definitions, without them, or with the workspace text, that reports a prompt
|
|
10709
|
+
* over `capacity`, the head included, throws this with `'GAUGE'`; the second holds `capacity` and
|
|
10710
|
+
* `prompt` in its `context`. A snapshot option that `isLedgerSnapshot` refuses
|
|
8915
10711
|
* throws this with `'SNAPSHOT'`, its `context` holding the `faults`. Narrow a caught value with {@link isLedgerError} and
|
|
8916
10712
|
* branch on `error.code`. A selection without an active `respond` call, for a conversation other
|
|
8917
10713
|
* than the ledger's, or for a request other than that call's request or a ledger note, reports
|
|
@@ -8953,7 +10749,7 @@ function isLedgerError(value) {
|
|
|
8953
10749
|
return (0, _orkestrel_contract.holds)(() => {
|
|
8954
10750
|
if (!(value instanceof Error && _orkestrel_contract.INTRINSICS.prototype(value) !== Error.prototype && _orkestrel_contract.INTRINSICS.describe(value, Symbol.for("@orkestrel/agent.LedgerError"))?.value === value)) return false;
|
|
8955
10751
|
const code = _orkestrel_contract.INTRINSICS.reflect.read(value, "code");
|
|
8956
|
-
if (!(code === "REQUEST" || code === "THRESHOLD" || code === "SHARE" || code === "CAPACITY" || code === "LIMIT" || code === "TOPIC" || code === "LOOKUP" || code === "GAUGE" || code === "REFERENCE" || code === "SNAPSHOT" || code === "QUESTION" || code === "WRITE")) return false;
|
|
10752
|
+
if (!(code === "REQUEST" || code === "THRESHOLD" || code === "SHARE" || code === "CAPACITY" || code === "LIMIT" || code === "TOPIC" || code === "LOOKUP" || code === "GAUGE" || code === "REFERENCE" || code === "SNAPSHOT" || code === "QUESTION" || code === "WRITE" || code === "OVERFLOW")) return false;
|
|
8957
10753
|
const context = _orkestrel_contract.INTRINSICS.reflect.read(value, "context");
|
|
8958
10754
|
return context === void 0 || (0, _orkestrel_contract.isObject)(context) || (0, _orkestrel_contract.isFunction)(context);
|
|
8959
10755
|
});
|
|
@@ -8989,9 +10785,10 @@ function digestLedgerWindow(exchanges, messages) {
|
|
|
8989
10785
|
* Digests tool definitions as the JSON a provider request carries them, in order.
|
|
8990
10786
|
*
|
|
8991
10787
|
* @remarks
|
|
8992
|
-
* A ledger stores this digest with its gauge
|
|
8993
|
-
* definitions: an added, removed, reordered, or
|
|
8994
|
-
* a {@link computeLedgerChecksum} checksum and
|
|
10788
|
+
* A ledger stores this digest with its gauge and compares it at a hydrate and at each `respond`
|
|
10789
|
+
* call, so it reuses the gauge only for the same definitions: an added, removed, reordered, or
|
|
10790
|
+
* edited definition changes the digest. The digest is a {@link computeLedgerChecksum} checksum and
|
|
10791
|
+
* makes no security claim.
|
|
8995
10792
|
*
|
|
8996
10793
|
* @param definitions - The tool definitions, as a tool manager's `definitions` method returns them
|
|
8997
10794
|
* @returns The 16-digit hexadecimal digest, or undefined when JSON cannot render the definitions
|
|
@@ -9005,6 +10802,82 @@ function digestLedgerTools(definitions) {
|
|
|
9005
10802
|
return text === void 0 ? void 0 : computeLedgerChecksum(text);
|
|
9006
10803
|
}
|
|
9007
10804
|
/**
|
|
10805
|
+
* Computes the tokens one estimate unit of a definition costs beyond the calibrated set.
|
|
10806
|
+
*
|
|
10807
|
+
* @remarks
|
|
10808
|
+
* The larger of `scale` and the calibrated set's overhead per estimate unit, or `scale` for an
|
|
10809
|
+
* empty set or one that estimates 0. A set's estimate is `estimateTokens` over its JSON text.
|
|
10810
|
+
*
|
|
10811
|
+
* @param calibrated - The definitions the gauge's overhead priced
|
|
10812
|
+
* @param gauge - The gauge's scale and overhead
|
|
10813
|
+
* @returns The tokens one estimate unit of a definition costs
|
|
10814
|
+
* @example
|
|
10815
|
+
* ```ts
|
|
10816
|
+
* computeToolDensity([], { scale: 1.1, overhead: 400 }) // 1.1
|
|
10817
|
+
* ```
|
|
10818
|
+
*/
|
|
10819
|
+
function computeToolDensity(calibrated, gauge) {
|
|
10820
|
+
const estimate = calibrated.length === 0 ? 0 : estimateTokens(JSON.stringify(calibrated));
|
|
10821
|
+
return estimate > 0 ? Math.max(gauge.scale, gauge.overhead / estimate) : gauge.scale;
|
|
10822
|
+
}
|
|
10823
|
+
/**
|
|
10824
|
+
* Computes what a call's advertised set spares of the calibrated overhead and adds beyond it.
|
|
10825
|
+
*
|
|
10826
|
+
* @remarks
|
|
10827
|
+
* Definitions compare by their JSON text, so a definition replaced under its name by another text
|
|
10828
|
+
* is both left out and added. An empty advertised set spares the whole overhead. Otherwise the
|
|
10829
|
+
* calibrated definitions the call leaves out spare the lower of `scale` and the calibrated set's
|
|
10830
|
+
* overhead per estimate unit, times their estimate, so a credit never exceeds their proportional
|
|
10831
|
+
* share of the overhead; the definitions it advertises beyond the calibrated set add
|
|
10832
|
+
* {@link computeToolDensity}, the higher of the two, times theirs. A set's estimate is
|
|
10833
|
+
* `estimateTokens` over its JSON text, 0 for an empty set. The call's price is
|
|
10834
|
+
* `overhead - spared + added`.
|
|
10835
|
+
*
|
|
10836
|
+
* @param calibrated - The definitions the gauge's overhead priced
|
|
10837
|
+
* @param advertised - The definitions the call advertises
|
|
10838
|
+
* @param gauge - The gauge's scale and overhead
|
|
10839
|
+
* @returns The tokens the call spares and adds
|
|
10840
|
+
* @example
|
|
10841
|
+
* ```ts
|
|
10842
|
+
* computeToolPrice([], [], { scale: 1.1, overhead: 400 }) // { spared: 400, added: 0 }
|
|
10843
|
+
* ```
|
|
10844
|
+
*/
|
|
10845
|
+
function computeToolPrice(calibrated, advertised, gauge) {
|
|
10846
|
+
if (advertised.length === 0) return {
|
|
10847
|
+
spared: gauge.overhead,
|
|
10848
|
+
added: 0
|
|
10849
|
+
};
|
|
10850
|
+
const sent = new Set(advertised.map((definition) => JSON.stringify(definition)));
|
|
10851
|
+
const priced = new Set(calibrated.map((definition) => JSON.stringify(definition)));
|
|
10852
|
+
const hidden = calibrated.filter((definition) => !sent.has(JSON.stringify(definition)));
|
|
10853
|
+
const extra = advertised.filter((definition) => !priced.has(JSON.stringify(definition)));
|
|
10854
|
+
return {
|
|
10855
|
+
spared: hidden.length === 0 ? 0 : Math.min(gauge.scale, gauge.overhead / estimateTokens(JSON.stringify(calibrated))) * estimateTokens(JSON.stringify(hidden)),
|
|
10856
|
+
added: extra.length === 0 ? 0 : computeToolDensity(calibrated, gauge) * estimateTokens(JSON.stringify(extra))
|
|
10857
|
+
};
|
|
10858
|
+
}
|
|
10859
|
+
/**
|
|
10860
|
+
* Computes the tokens loading a skill adds: its body, and the definitions it names beyond an advertised set.
|
|
10861
|
+
*
|
|
10862
|
+
* @remarks
|
|
10863
|
+
* The body is the text `renderSkillBody` renders, priced at `scale`, and a blank `content` renders
|
|
10864
|
+
* no body. The caller passes the definitions the skill names that the turn does not advertise,
|
|
10865
|
+
* priced as {@link computeToolPrice} prices an addition.
|
|
10866
|
+
*
|
|
10867
|
+
* @param skill - The skill to load
|
|
10868
|
+
* @param added - The definitions the skill names that the advertised set leaves out
|
|
10869
|
+
* @param calibrated - The definitions the gauge's overhead priced
|
|
10870
|
+
* @param gauge - The gauge's scale and overhead
|
|
10871
|
+
* @returns The tokens the load adds
|
|
10872
|
+
* @example
|
|
10873
|
+
* ```ts
|
|
10874
|
+
* computeSkillPrice({ name: 'refunds', content: 'Check the order first.' }, [], [], { scale: 1, overhead: 0 }) // 9
|
|
10875
|
+
* ```
|
|
10876
|
+
*/
|
|
10877
|
+
function computeSkillPrice(skill, added, calibrated, gauge) {
|
|
10878
|
+
return (skill.content.trim() === "" ? 0 : gauge.scale * estimateTokens(renderSkillBody(skill))) + computeToolPrice(calibrated, [...calibrated, ...added], gauge).added;
|
|
10879
|
+
}
|
|
10880
|
+
/**
|
|
9008
10881
|
* Computes a checksum of text: two 32-bit FNV-1a lanes over its code points, as 16 hexadecimal digits.
|
|
9009
10882
|
*
|
|
9010
10883
|
* @remarks
|
|
@@ -9233,23 +11106,6 @@ function splitWords(part) {
|
|
|
9233
11106
|
return part.split(/\s+/).map((word) => word.replace(/^[\p{P}\p{S}]+|[\p{P}\p{S}]+$/gu, "").toLowerCase()).filter((word) => word !== "");
|
|
9234
11107
|
}
|
|
9235
11108
|
/**
|
|
9236
|
-
* Finds the call that owns a tool message within a collected tool group.
|
|
9237
|
-
* @param group - The assistant leader followed by its tool results
|
|
9238
|
-
* @param message - The tool result to pair; distinct leader ids pair by result id when any result has one
|
|
9239
|
-
* @returns The call matching the result's call id, or its position when the leader repeats an id or every result lacks an id; undefined when unpaired
|
|
9240
|
-
* @example
|
|
9241
|
-
* ```ts
|
|
9242
|
-
* findLedgerCall(group, result)?.arguments
|
|
9243
|
-
* ```
|
|
9244
|
-
*/
|
|
9245
|
-
function findLedgerCall(group, message) {
|
|
9246
|
-
const [leader, ...results] = group;
|
|
9247
|
-
const at = results.findIndex((result) => result.id === message.id);
|
|
9248
|
-
if (message.role !== "tool" || at < 0) return void 0;
|
|
9249
|
-
const calls = leader?.calls ?? [];
|
|
9250
|
-
return !(new Set(calls.map((call) => call.id)).size !== calls.length) && results.some((result) => result.call !== void 0) ? calls.find((call) => call.id === message.call) : calls[at];
|
|
9251
|
-
}
|
|
9252
|
-
/**
|
|
9253
11109
|
* Ranks a source the records render for removal before the next prompt.
|
|
9254
11110
|
*
|
|
9255
11111
|
* @param group - The planning group: 1 for a topic match, 2 for an off-topic rule or correction, or 3 for a name match
|
|
@@ -9943,23 +11799,34 @@ function matchesCutLine(line) {
|
|
|
9943
11799
|
/**
|
|
9944
11800
|
* Renders the tail stub of a lookup result from an earlier request.
|
|
9945
11801
|
*
|
|
11802
|
+
* @remarks
|
|
11803
|
+
* A `hidden` stub names the `recall` call that finds its result, with the first string argument,
|
|
11804
|
+
* trimmed, or `its id` when no argument is a string or that one is blank, only while `offered` is `true`. The ledger plans the
|
|
11805
|
+
* tail with every stub naming `recall`, then renders the tail's stubs again after it admits a window
|
|
11806
|
+
* rung, with `offered` set to whether that rung offers `recall` and the scope admits it, so the
|
|
11807
|
+
* second render can only shorten a stub and raises no price. `offered` changes no other state.
|
|
11808
|
+
*
|
|
9946
11809
|
* @param name - The lookup tool name
|
|
9947
11810
|
* @param args - The arguments the call carried; arguments JSON cannot carry leave the call as its bare tool name, per {@link renderLookupCall}
|
|
9948
11811
|
* @param state - What became of the result: `failed`, `empty`, `shown` in the records, or `hidden` from them
|
|
9949
11812
|
* @param write - If `true`, the call is a write's, whose `empty` state reads `changed nothing`; if `false`, a lookup's, whose `empty` state reads `no record`. Default: `false`
|
|
11813
|
+
* @param offered - If `true`, a `hidden` result names the `recall` route; if `false`, because the admitted rung offers no `recall` or the scope excludes it, it reads `result not shown`. Default: `true`
|
|
9950
11814
|
* @returns The stub text, which carries the call and its state
|
|
9951
11815
|
*
|
|
9952
11816
|
* @example
|
|
9953
11817
|
* ```ts
|
|
9954
11818
|
* renderStub('lookup_order', { id: 'BW-5512' }, 'hidden')
|
|
9955
11819
|
* // 'lookup_order {"id":"BW-5512"}: result not shown; call recall with BW-5512'
|
|
11820
|
+
* renderStub('lookup_order', { id: 'BW-5512' }, 'hidden', false, false)
|
|
11821
|
+
* // 'lookup_order {"id":"BW-5512"}: result not shown'
|
|
9956
11822
|
* ```
|
|
9957
11823
|
*/
|
|
9958
|
-
function renderStub(name, args, state, write = false) {
|
|
11824
|
+
function renderStub(name, args, state, write = false, offered = true) {
|
|
9959
11825
|
const head = renderLookupCall(name, args);
|
|
9960
11826
|
if (state === "failed") return `${head}: failed`;
|
|
9961
11827
|
if (state === "empty") return write ? `${head}: changed nothing` : `${head}: no record`;
|
|
9962
11828
|
if (state === "shown") return `${head}: result shown under Records below`;
|
|
11829
|
+
if (!offered) return `${head}: result not shown`;
|
|
9963
11830
|
return `${head}: result not shown; call recall with ${(Object.values(args).find(_orkestrel_contract.isString) ?? "").trim() || "its id"}`;
|
|
9964
11831
|
}
|
|
9965
11832
|
/**
|
|
@@ -10329,6 +12196,51 @@ function collectLedgerEvidence(messages, sources, amendments) {
|
|
|
10329
12196
|
]), { ...edge }]))
|
|
10330
12197
|
};
|
|
10331
12198
|
}
|
|
12199
|
+
/**
|
|
12200
|
+
* Cuts the workspace text a calibration sample adds over its head to a number of characters, or
|
|
12201
|
+
* returns `undefined` when the sample adds anything else.
|
|
12202
|
+
*
|
|
12203
|
+
* @remarks
|
|
12204
|
+
* The head and the shown sample are what a context's `build` returns for an empty turn without and
|
|
12205
|
+
* with the workspace: the shown sample holds the head's messages with the workspace section appended
|
|
12206
|
+
* to the context message, or one context message added where the head holds none. The cut keeps the
|
|
12207
|
+
* first `limit` characters of that added text and every other message as the sample holds it. A
|
|
12208
|
+
* sample whose messages differ from the head's in any other way, or that adds no text, returns
|
|
12209
|
+
* `undefined`.
|
|
12210
|
+
*
|
|
12211
|
+
* @param head - The head built with no workspace
|
|
12212
|
+
* @param shown - The head built with the workspace a request renders
|
|
12213
|
+
* @param limit - The characters of added text to keep
|
|
12214
|
+
* @returns The cut sample, or `undefined` when the sample adds no trailing text to one message
|
|
12215
|
+
*
|
|
12216
|
+
* @example
|
|
12217
|
+
* ```ts
|
|
12218
|
+
* import { cutWorkspaceSample } from '@orkestrel/agent'
|
|
12219
|
+
*
|
|
12220
|
+
* const head = [{ id: 's', role: 'system', content: 'Serve the desk.' }] as const
|
|
12221
|
+
* const shown = [...head, { id: 'c', role: 'user', content: '## Workspace\n\nFile: a.md' }] as const
|
|
12222
|
+
* cutWorkspaceSample(head, shown, 12)?.[1]?.content // '## Workspace'
|
|
12223
|
+
* ```
|
|
12224
|
+
*/
|
|
12225
|
+
function cutWorkspaceSample(head, shown, limit) {
|
|
12226
|
+
const at = shown.findIndex((message, index) => message.role !== head[index]?.role || message.content !== head[index]?.content);
|
|
12227
|
+
const added = shown[at];
|
|
12228
|
+
const extended = shown.length === head.length ? head[at] : void 0;
|
|
12229
|
+
const prefix = extended?.content ?? "";
|
|
12230
|
+
const rest = shown.slice(at + 1);
|
|
12231
|
+
const held = head.slice(extended === void 0 ? at : at + 1);
|
|
12232
|
+
if (added === void 0 || shown.length - head.length > 1 || shown.length < head.length) return void 0;
|
|
12233
|
+
if (!added.content.startsWith(prefix) || added.content.length === prefix.length) return void 0;
|
|
12234
|
+
if (rest.length !== held.length || rest.some((message, index) => message.role !== held[index]?.role || message.content !== held[index]?.content)) return void 0;
|
|
12235
|
+
return [
|
|
12236
|
+
...shown.slice(0, at),
|
|
12237
|
+
{
|
|
12238
|
+
...added,
|
|
12239
|
+
content: prefix + added.content.slice(prefix.length, prefix.length + limit)
|
|
12240
|
+
},
|
|
12241
|
+
...rest
|
|
12242
|
+
];
|
|
12243
|
+
}
|
|
10332
12244
|
// src/core/ledgers/Classifier.ts
|
|
10333
12245
|
/**
|
|
10334
12246
|
* Files messages through the conversation's judgment manager and reads their categories and corrections.
|
|
@@ -10349,6 +12261,7 @@ var Classifier = class {
|
|
|
10349
12261
|
* @param options - The conversation, judge, questions, cutoffs, and handlers
|
|
10350
12262
|
*/
|
|
10351
12263
|
constructor(options) {
|
|
12264
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, CLASSIFIER_OPTION_KEYS, "Classifier", "ClassifierOptions");
|
|
10352
12265
|
this.#options = options;
|
|
10353
12266
|
}
|
|
10354
12267
|
/**
|
|
@@ -10725,6 +12638,7 @@ var Gauge = class {
|
|
|
10725
12638
|
* @throws {LedgerError} Thrown when `predict` is not a nonnegative safe integer less than `capacity` (code `'CAPACITY'`)
|
|
10726
12639
|
*/
|
|
10727
12640
|
constructor(options) {
|
|
12641
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, GAUGE_OPTION_KEYS, "Gauge", "GaugeOptions");
|
|
10728
12642
|
if (!(0, _orkestrel_contract.isFiniteNumber)(options.scale) || options.scale <= 0) throw new LedgerError("GAUGE", "gauge scale must be finite and greater than 0");
|
|
10729
12643
|
if (!(0, _orkestrel_contract.isFiniteNumber)(options.overhead) || options.overhead < 0) throw new LedgerError("GAUGE", "gauge overhead must be finite and at least 0");
|
|
10730
12644
|
if (!isPositiveSafeInteger(options.capacity)) throw new LedgerError("CAPACITY", "gauge capacity must be a positive safe integer");
|
|
@@ -10755,7 +12669,7 @@ var Gauge = class {
|
|
|
10755
12669
|
}
|
|
10756
12670
|
remainder(calls) {
|
|
10757
12671
|
const call = calls.at(-1);
|
|
10758
|
-
const used = (0, _orkestrel_contract.isFiniteNumber)(call?.prompt) ? call.prompt + (call.completion ?? 0) - (this.#replay === "none" ? call.thinking ?? 0 : 0) : Math.max(0, this.#overhead - (call?.spared ?? 0)) + this.#scale * (call?.estimate ?? 0);
|
|
12672
|
+
const used = (0, _orkestrel_contract.isFiniteNumber)(call?.prompt) ? call.prompt + (call.completion ?? 0) - (this.#replay === "none" ? call.thinking ?? 0 : 0) : Math.max(0, this.#overhead - (call?.spared ?? 0)) + (call?.added ?? 0) + this.#scale * (call?.estimate ?? 0);
|
|
10759
12673
|
return Math.max(0, this.#capacity - used);
|
|
10760
12674
|
}
|
|
10761
12675
|
reserve(calls, longest) {
|
|
@@ -10775,7 +12689,7 @@ var Gauge = class {
|
|
|
10775
12689
|
}
|
|
10776
12690
|
observe(calls, reply) {
|
|
10777
12691
|
const first = calls[0];
|
|
10778
|
-
if (first !== void 0 && (0, _orkestrel_contract.isFiniteNumber)(first.prompt) && first.estimate > 0 && ((first.spared ?? 0) === 0 || (first.spared ?? 0) >= this.#overhead)) {
|
|
12692
|
+
if (first !== void 0 && (0, _orkestrel_contract.isFiniteNumber)(first.prompt) && first.estimate > 0 && (first.added ?? 0) === 0 && ((first.spared ?? 0) === 0 || (first.spared ?? 0) >= this.#overhead)) {
|
|
10779
12693
|
const priced = first.prompt - Math.max(0, this.#overhead - (first.spared ?? 0));
|
|
10780
12694
|
if (priced > 0) this.#scale = priced / first.estimate;
|
|
10781
12695
|
}
|
|
@@ -10983,6 +12897,7 @@ var LedgerRequest = class {
|
|
|
10983
12897
|
#admitted = /* @__PURE__ */ new Set();
|
|
10984
12898
|
#denials = /* @__PURE__ */ new Set();
|
|
10985
12899
|
#listed = [];
|
|
12900
|
+
#commits = [];
|
|
10986
12901
|
#recalls = 0;
|
|
10987
12902
|
#closed = false;
|
|
10988
12903
|
/**
|
|
@@ -11169,6 +13084,23 @@ var LedgerRequest = class {
|
|
|
11169
13084
|
lines(result) {
|
|
11170
13085
|
return this.#listed.findLast((listing) => listing.result === result)?.lines;
|
|
11171
13086
|
}
|
|
13087
|
+
commit(price, skill) {
|
|
13088
|
+
this.#commits = [...this.#commits, {
|
|
13089
|
+
price,
|
|
13090
|
+
turn: this.calls.length,
|
|
13091
|
+
...skill === void 0 ? {} : { skill }
|
|
13092
|
+
}];
|
|
13093
|
+
}
|
|
13094
|
+
get committed() {
|
|
13095
|
+
return this.#inflight().reduce((sum, entry) => sum + entry.price, 0);
|
|
13096
|
+
}
|
|
13097
|
+
get loads() {
|
|
13098
|
+
return this.#inflight().flatMap((entry) => entry.skill === void 0 ? [] : [entry.skill]);
|
|
13099
|
+
}
|
|
13100
|
+
#inflight() {
|
|
13101
|
+
const turn = this.calls.length;
|
|
13102
|
+
return this.#commits.filter((entry) => entry.turn === turn);
|
|
13103
|
+
}
|
|
11172
13104
|
};
|
|
11173
13105
|
// src/core/ledgers/LedgerScan.ts
|
|
11174
13106
|
/**
|
|
@@ -11446,16 +13378,20 @@ var Ledger = class {
|
|
|
11446
13378
|
#share;
|
|
11447
13379
|
#conversation;
|
|
11448
13380
|
#agent;
|
|
13381
|
+
#record;
|
|
13382
|
+
#builtins;
|
|
13383
|
+
#given;
|
|
11449
13384
|
#classifier;
|
|
11450
13385
|
#requests = /* @__PURE__ */ new Set();
|
|
11451
13386
|
#annotations = /* @__PURE__ */ new Set();
|
|
11452
13387
|
#results = /* @__PURE__ */ new Map();
|
|
11453
13388
|
#pending = /* @__PURE__ */ new Map();
|
|
11454
|
-
#live = /* @__PURE__ */ new
|
|
13389
|
+
#live = /* @__PURE__ */ new Map();
|
|
11455
13390
|
#scans = /* @__PURE__ */ new Set();
|
|
11456
13391
|
#calibrations = /* @__PURE__ */ new Set();
|
|
11457
13392
|
#gauge;
|
|
11458
|
-
#
|
|
13393
|
+
#measured;
|
|
13394
|
+
#refusal;
|
|
11459
13395
|
#window = [];
|
|
11460
13396
|
#evidence = collectLedgerEvidence([], /* @__PURE__ */ new Set(), []);
|
|
11461
13397
|
/**
|
|
@@ -11470,6 +13406,8 @@ var Ledger = class {
|
|
|
11470
13406
|
* @throws {ConversationError} Thrown when `isLedgerSnapshot` admits `snapshot` but its conversation holds a `system` message (code `'ROLE'`) or repeats an id (code `'SNAPSHOT'`)
|
|
11471
13407
|
*/
|
|
11472
13408
|
constructor(provider, options) {
|
|
13409
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options, LEDGER_OPTION_KEYS, "Ledger", "LedgerOptions");
|
|
13410
|
+
(0, _orkestrel_contract.refuseOptionKeys)(options.agent, LEDGER_AGENT_KEYS, "Ledger agent", "LedgerAgentOptions");
|
|
11473
13411
|
for (const key of [
|
|
11474
13412
|
"category",
|
|
11475
13413
|
"topic",
|
|
@@ -11547,6 +13485,9 @@ var Ledger = class {
|
|
|
11547
13485
|
}) }),
|
|
11548
13486
|
execute: (args) => this.#recall(args)
|
|
11549
13487
|
}));
|
|
13488
|
+
this.#builtins = requireBuiltins(options.agent?.builtins, tools);
|
|
13489
|
+
if (this.#builtins.length > 0 && host === void 0) throw new AgentError("AUTHORITY", "agent.builtins are set and no agent.authority is; pass authority: createAuthority({ fallback: { zone: 'default', verdict: 'allow' } }) to allow every call, or rules with a deny fallback");
|
|
13490
|
+
this.#given = requireWorkspace(options.agent?.workspace);
|
|
11550
13491
|
const advertised = new Authority({
|
|
11551
13492
|
rules: [{
|
|
11552
13493
|
match: (context) => names.includes(context.call.name),
|
|
@@ -11565,17 +13506,14 @@ var Ledger = class {
|
|
|
11565
13506
|
system: options.system,
|
|
11566
13507
|
tools,
|
|
11567
13508
|
authority: {
|
|
11568
|
-
evaluate: (context) => this.#
|
|
11569
|
-
zone: "ledger",
|
|
11570
|
-
verdict: "deny",
|
|
11571
|
-
reason: LEDGER_NOTES.repeat
|
|
11572
|
-
} : host === void 0 || context.call.name === "recall" || !names.includes(context.call.name) ? advertised.evaluate(context) : host.evaluate(context),
|
|
13509
|
+
evaluate: (context) => this.#evaluate(context, host, advertised, names),
|
|
11573
13510
|
ask: (context, decision, signal) => this.#current()?.denied(context.call.name, context.call.arguments) === true ? Promise.resolve(false) : host?.ask === void 0 ? advertised.ask(context, decision, signal) : host.ask(context, decision, signal)
|
|
11574
13511
|
},
|
|
11575
13512
|
limit: options.agent?.limit ?? 8,
|
|
11576
13513
|
select: (conversation, request, signal) => this.#select(conversation, request, signal)
|
|
11577
13514
|
});
|
|
11578
|
-
const
|
|
13515
|
+
const calibrated = this.#calibrated();
|
|
13516
|
+
const digest = digestLedgerTools(calibrated);
|
|
11579
13517
|
const gauge = options.gauge ?? (snapshot?.gauge?.digest === digest ? snapshot?.gauge : void 0);
|
|
11580
13518
|
if (gauge !== void 0) {
|
|
11581
13519
|
this.#gauge = new Gauge({
|
|
@@ -11585,10 +13523,10 @@ var Ledger = class {
|
|
|
11585
13523
|
predict: this.#predict,
|
|
11586
13524
|
replay: this.#replay
|
|
11587
13525
|
});
|
|
11588
|
-
this.#
|
|
13526
|
+
this.#measured = calibrated;
|
|
11589
13527
|
}
|
|
11590
13528
|
this.#agent.emitter.on("deny", (call, _reason, decision) => {
|
|
11591
|
-
if (decision !== void 0 && call.name !== "recall" && names.includes(call.name)) this.#current()?.deny(call.name, call.arguments);
|
|
13529
|
+
if (decision !== void 0 && decision.reason !== LEDGER_NOTES.overflow && call.name !== "recall" && (names.includes(call.name) || this.#enables(call.name))) this.#current()?.deny(call.name, call.arguments);
|
|
11592
13530
|
});
|
|
11593
13531
|
this.#agent.emitter.on("tool", (call, result) => {
|
|
11594
13532
|
this.#flush();
|
|
@@ -11601,21 +13539,34 @@ var Ledger = class {
|
|
|
11601
13539
|
const request = this.#current();
|
|
11602
13540
|
if (request?.calls.at(-1)?.tools === 0) request.abort();
|
|
11603
13541
|
this.#release(call, result);
|
|
13542
|
+
this.#reopen(call, result);
|
|
11604
13543
|
});
|
|
11605
13544
|
this.#agent.emitter.on("select", (selection) => {
|
|
11606
13545
|
if (isLedgerError(selection.fault) && selection.fault.code === "REQUEST") return;
|
|
11607
13546
|
this.#current()?.adopt(selection, this.#conversation.messages().length);
|
|
11608
13547
|
});
|
|
11609
13548
|
this.#agent.emitter.on("fault", (error) => this.#current()?.fault(error));
|
|
11610
|
-
this.#agent.emitter.on("
|
|
13549
|
+
this.#agent.emitter.on("request", (index, sent) => {
|
|
13550
|
+
if (this.#gate(index, sent)) this.#observeRequest(sent);
|
|
13551
|
+
});
|
|
11611
13552
|
this.#agent.emitter.on("usage", (usage) => this.#current()?.spend(usage));
|
|
13553
|
+
const agent = this.#agent;
|
|
13554
|
+
this.#record = Object.freeze({
|
|
13555
|
+
emitter: agent.emitter,
|
|
13556
|
+
id: agent.id,
|
|
13557
|
+
get status() {
|
|
13558
|
+
return agent.status;
|
|
13559
|
+
},
|
|
13560
|
+
context: agent.context,
|
|
13561
|
+
abort: (reason) => agent.abort(reason)
|
|
13562
|
+
});
|
|
11612
13563
|
}
|
|
11613
13564
|
/**
|
|
11614
|
-
* Returns the owned request engine.
|
|
11615
|
-
* @returns The
|
|
13565
|
+
* Returns the record of the owned request engine, which starts no run.
|
|
13566
|
+
* @returns The frozen record of the agent serving this conversation
|
|
11616
13567
|
*/
|
|
11617
13568
|
get agent() {
|
|
11618
|
-
return this.#
|
|
13569
|
+
return this.#record;
|
|
11619
13570
|
}
|
|
11620
13571
|
/**
|
|
11621
13572
|
* Returns the owned message history.
|
|
@@ -11636,9 +13587,10 @@ var Ledger = class {
|
|
|
11636
13587
|
}
|
|
11637
13588
|
/**
|
|
11638
13589
|
* Measures message scale and tool overhead from provider prompt usage.
|
|
11639
|
-
* @param signal - The signal bounding
|
|
13590
|
+
* @param signal - The signal bounding every calibration call; an abort rejects with its reason
|
|
11640
13591
|
* @returns The stored scale and overhead cost
|
|
11641
|
-
* @throws {LedgerError} Thrown when
|
|
13592
|
+
* @throws {LedgerError} Thrown when a calibration call reports no prompt usage, or a prompt usage of 0 or less (code `'GAUGE'`)
|
|
13593
|
+
* @throws {LedgerError} Thrown when a calibration call, with the tool definitions, without them, or with the workspace text, reports a prompt over `capacity`, the head included (code `'GAUGE'`)
|
|
11642
13594
|
* @throws {AgentError} Thrown when a request or calibration is active (code `'CONCURRENCY'`)
|
|
11643
13595
|
*/
|
|
11644
13596
|
async calibrate(signal) {
|
|
@@ -11661,7 +13613,7 @@ var Ledger = class {
|
|
|
11661
13613
|
const conversation = this.#conversation.snapshot();
|
|
11662
13614
|
const ids = conversation.messages.map((message) => message.id);
|
|
11663
13615
|
const gauge = this.gauge;
|
|
11664
|
-
const priced = this.#digest;
|
|
13616
|
+
const priced = this.#digest();
|
|
11665
13617
|
const window = {
|
|
11666
13618
|
exchanges: this.#window.map((entry) => ({
|
|
11667
13619
|
request: entry.request.id,
|
|
@@ -11695,13 +13647,15 @@ var Ledger = class {
|
|
|
11695
13647
|
/**
|
|
11696
13648
|
* Appends a request and serves it, recovering an unfinished or rejected first pass with one answer pass.
|
|
11697
13649
|
* In either pass, a call the model makes in a turn that advertises no tools runs nothing and ends the pass after that generation, whatever the agent's `limit`, and when every call of that turn failed, the ledger stores the reply without its calls: a reply with text ends the pass complete, and one without text ends it partial with empty content.
|
|
11698
|
-
* A calibration call that reports no prompt usage, or a prompt usage of 0 or less, rejects with `LedgerError` code `'GAUGE'`; a provider error during calibration rejects with that error.
|
|
13650
|
+
* A calibration call that reports no prompt usage, or a prompt usage of 0 or less, rejects with `LedgerError` code `'GAUGE'`; a provider error during calibration rejects with that error. A calibration call, with the tool definitions, without them, or with the workspace text, that reports a prompt over `capacity`, the head included, rejects with the same code and stores no request.
|
|
11699
13651
|
* When the answer pass rejects, `respond` rejects with that error, and under the default `strict` a judge failure or another selection failure rejects it too. A rejection belongs to its own call: the ledger stays free, and the next call asks the judge again about whatever is still unanswered.
|
|
11700
13652
|
* @param content - The request text
|
|
11701
13653
|
* @param signal - The caller's cancellation signal; an abort during calibration rejects with its reason
|
|
11702
13654
|
* @returns The final pass, the usage of every pass, and the filing attempts of this request alone
|
|
11703
13655
|
* @throws {AgentError} Thrown when a request or calibration is active (code `'CONCURRENCY'`)
|
|
11704
13656
|
* @throws {LedgerError} Thrown when a calibration call reports no prompt usage, or a prompt usage of 0 or less (code `'GAUGE'`)
|
|
13657
|
+
* @throws {LedgerError} Thrown when a calibration call, with the tool definitions, without them, or with the workspace text, reports a prompt over `capacity`, the head included (code `'GAUGE'`)
|
|
13658
|
+
* @throws {LedgerError} Thrown when the answer pass prices over `capacity - predict` with its note left out, or a host act after the note grows its request past that limit (code `'OVERFLOW'`)
|
|
11705
13659
|
* @throws {ContractError} Thrown when `signal` is defined and is not a native `AbortSignal`
|
|
11706
13660
|
*/
|
|
11707
13661
|
async respond(content, signal) {
|
|
@@ -11709,12 +13663,15 @@ var Ledger = class {
|
|
|
11709
13663
|
const caller = (0, _orkestrel_abort.createAbort)({ ...signal === void 0 ? {} : { signal } }).signal;
|
|
11710
13664
|
const request = new LedgerRequest(content, this.#options.recall?.limit);
|
|
11711
13665
|
const scope = this.#agent.context.scope;
|
|
11712
|
-
this.#live.
|
|
13666
|
+
this.#live.set(request, scope);
|
|
11713
13667
|
try {
|
|
11714
|
-
if (this.#gauge === void 0)
|
|
13668
|
+
if (this.#gauge === void 0 || digestLedgerTools(this.#calibrated()) !== this.#digest()) {
|
|
13669
|
+
this.#gauge = void 0;
|
|
13670
|
+
await this.#measureGauge(caller);
|
|
13671
|
+
}
|
|
11715
13672
|
const entered = request.enter(this.#conversation);
|
|
11716
13673
|
this.#requests.add(entered.id);
|
|
11717
|
-
const first = this.#dropStrayCalls(request, await this.#generatePass(request, caller, this.#options.think).catch((error) => {
|
|
13674
|
+
const first = this.#dropStrayCalls(request, await this.#generatePass(request, caller, this.#options.think, scope).catch((error) => {
|
|
11718
13675
|
if (request.faulted(error)) throw error;
|
|
11719
13676
|
return {
|
|
11720
13677
|
content: "",
|
|
@@ -11733,13 +13690,12 @@ var Ledger = class {
|
|
|
11733
13690
|
role: "user",
|
|
11734
13691
|
content: LEDGER_NOTES.cue
|
|
11735
13692
|
}).id);
|
|
11736
|
-
const
|
|
13693
|
+
const filter = intersectFilters(scope, { tools: [] });
|
|
11737
13694
|
const answer = createScope({
|
|
11738
|
-
...
|
|
13695
|
+
...filter,
|
|
11739
13696
|
name: "answer",
|
|
11740
|
-
tools: [],
|
|
11741
13697
|
select: async (conversation, message, runSignal) => {
|
|
11742
|
-
const selection = await this.#select(conversation, message, runSignal);
|
|
13698
|
+
const selection = await this.#select(conversation, message, runSignal, filter);
|
|
11743
13699
|
if (selection.fault !== void 0) return selection;
|
|
11744
13700
|
return {
|
|
11745
13701
|
...selection,
|
|
@@ -11747,12 +13703,9 @@ var Ledger = class {
|
|
|
11747
13703
|
};
|
|
11748
13704
|
}
|
|
11749
13705
|
});
|
|
11750
|
-
this.#
|
|
11751
|
-
|
|
11752
|
-
|
|
11753
|
-
} finally {
|
|
11754
|
-
if (this.#agent.context.scope === answer) this.#agent.context.apply(previous);
|
|
11755
|
-
}
|
|
13706
|
+
passes.push(this.#dropStrayCalls(request, await this.#generatePass(request, caller, false, answer)));
|
|
13707
|
+
const refusal = this.#refusal;
|
|
13708
|
+
if (refusal !== void 0 && !caller.aborted) throw refusal;
|
|
11756
13709
|
}
|
|
11757
13710
|
const last = passes.at(-1) ?? first;
|
|
11758
13711
|
request.think(this.#conversation.messages());
|
|
@@ -11778,26 +13731,31 @@ var Ledger = class {
|
|
|
11778
13731
|
}
|
|
11779
13732
|
async #measureGauge(signal) {
|
|
11780
13733
|
try {
|
|
11781
|
-
const messages =
|
|
11782
|
-
|
|
11783
|
-
|
|
11784
|
-
|
|
11785
|
-
|
|
11786
|
-
|
|
11787
|
-
|
|
11788
|
-
|
|
11789
|
-
|
|
13734
|
+
const messages = this.#agent.context.build({
|
|
13735
|
+
selection: {
|
|
13736
|
+
messages: [],
|
|
13737
|
+
judgments: []
|
|
13738
|
+
},
|
|
13739
|
+
scope: createScope({
|
|
13740
|
+
name: "calibration",
|
|
13741
|
+
workspaces: []
|
|
13742
|
+
})
|
|
13743
|
+
});
|
|
13744
|
+
const definitions = this.#calibrated();
|
|
11790
13745
|
const priced = await this.#provider.generate(messages, signal, definitions, { think: false });
|
|
13746
|
+
this.#refuseOverCapacity("with", priced.usage?.prompt);
|
|
11791
13747
|
const bare = await this.#provider.generate(messages, signal, void 0, { think: false });
|
|
11792
|
-
|
|
13748
|
+
this.#refuseOverCapacity("without", bare.usage?.prompt);
|
|
13749
|
+
if (!(0, _orkestrel_contract.isFiniteNumber)(priced.usage?.prompt) || priced.usage.prompt <= 0 || !(0, _orkestrel_contract.isFiniteNumber)(bare.usage?.prompt) || bare.usage.prompt <= 0) throw new LedgerError("GAUGE", "calibration requires prompt usage from each call");
|
|
13750
|
+
const scale = await this.#measureScale(messages, bare.usage.prompt, signal);
|
|
11793
13751
|
this.#gauge = new Gauge({
|
|
11794
13752
|
capacity: this.#options.capacity,
|
|
11795
13753
|
predict: this.#predict,
|
|
11796
13754
|
replay: this.#replay,
|
|
11797
|
-
scale
|
|
13755
|
+
scale,
|
|
11798
13756
|
overhead: Math.max(0, priced.usage.prompt - bare.usage.prompt)
|
|
11799
13757
|
});
|
|
11800
|
-
this.#
|
|
13758
|
+
this.#measured = definitions;
|
|
11801
13759
|
return {
|
|
11802
13760
|
scale: this.#gauge.scale,
|
|
11803
13761
|
overhead: this.#gauge.overhead
|
|
@@ -11807,13 +13765,52 @@ var Ledger = class {
|
|
|
11807
13765
|
throw error;
|
|
11808
13766
|
}
|
|
11809
13767
|
}
|
|
11810
|
-
async #
|
|
13768
|
+
async #measureScale(head, bare, signal) {
|
|
13769
|
+
const rate = bare / estimateMessages(head);
|
|
13770
|
+
const workspace = this.#show(intersectFilters(this.#agent.context.scope, void 0)).workspace;
|
|
13771
|
+
const files = (resolveWorkspace(this.#agent.context.workspaces, workspace, void 0)?.files() ?? []).flatMap((file) => (0, _orkestrel_workspace.isTextContent)(file.content) ? [file.path] : []);
|
|
13772
|
+
const shown = this.#agent.context.build({
|
|
13773
|
+
selection: {
|
|
13774
|
+
messages: [],
|
|
13775
|
+
judgments: []
|
|
13776
|
+
},
|
|
13777
|
+
scope: createScope({
|
|
13778
|
+
name: "calibration",
|
|
13779
|
+
files
|
|
13780
|
+
}),
|
|
13781
|
+
disclosure: {
|
|
13782
|
+
skills: [],
|
|
13783
|
+
...workspace === void 0 ? {} : { workspace }
|
|
13784
|
+
}
|
|
13785
|
+
});
|
|
13786
|
+
const whole = estimateMessages(shown) - estimateMessages(head);
|
|
13787
|
+
const cut = cutWorkspaceSample(head, shown, Math.floor((this.#options.capacity - bare) / 3));
|
|
13788
|
+
const part = cut === void 0 ? 0 : estimateMessages(cut) - estimateMessages(head);
|
|
13789
|
+
if (cut === void 0 || whole <= 0 || part <= 0) return rate;
|
|
13790
|
+
const third = await this.#provider.generate(cut, signal, void 0, { think: false });
|
|
13791
|
+
this.#refuseOverCapacity("workspace", third.usage?.prompt);
|
|
13792
|
+
if (!(0, _orkestrel_contract.isFiniteNumber)(third.usage?.prompt) || third.usage.prompt <= 0) throw new LedgerError("GAUGE", "calibration requires prompt usage from each call");
|
|
13793
|
+
const tokens = bare + (third.usage.prompt - bare) * whole / part;
|
|
13794
|
+
return Math.max(rate, tokens / estimateMessages(shown));
|
|
13795
|
+
}
|
|
13796
|
+
#refuseOverCapacity(call, prompt) {
|
|
13797
|
+
const capacity = this.#options.capacity;
|
|
13798
|
+
const subject = call === "workspace" ? "with the workspace text" : `${call} the tool definitions`;
|
|
13799
|
+
const remedy = call === "workspace" ? "narrow what the scope shows of the workspace, or raise capacity" : "hold tools behind a skill with the `load` built-in, register fewer tools, or raise capacity";
|
|
13800
|
+
if ((0, _orkestrel_contract.isFiniteNumber)(prompt) && prompt > capacity) throw new LedgerError("GAUGE", `the calibration call ${subject} prices ${prompt} tokens, the head included, over the capacity ${capacity}; ${remedy}`, { context: {
|
|
13801
|
+
capacity,
|
|
13802
|
+
prompt
|
|
13803
|
+
} });
|
|
13804
|
+
}
|
|
13805
|
+
async #generatePass(request, signal, think, scope) {
|
|
13806
|
+
this.#refusal = void 0;
|
|
11811
13807
|
request.open(this.#conversation.messages().length);
|
|
11812
13808
|
const stop = request.signal;
|
|
11813
13809
|
return await this.#agent.generate({
|
|
11814
|
-
signal: stop === void 0 ? signal :
|
|
13810
|
+
signal: stop === void 0 ? signal : (0, _orkestrel_abort.linkSignal)(signal, stop),
|
|
11815
13811
|
conversation: this.#conversation,
|
|
11816
|
-
...think === void 0 ? {} : { think }
|
|
13812
|
+
...think === void 0 ? {} : { think },
|
|
13813
|
+
...scope === void 0 ? {} : { scope }
|
|
11817
13814
|
});
|
|
11818
13815
|
}
|
|
11819
13816
|
#dropStrayCalls(request, pass) {
|
|
@@ -11839,12 +13836,103 @@ var Ledger = class {
|
|
|
11839
13836
|
};
|
|
11840
13837
|
}
|
|
11841
13838
|
#current() {
|
|
11842
|
-
const [request] = this.#live;
|
|
13839
|
+
const [request] = this.#live.keys();
|
|
11843
13840
|
return request?.message === void 0 ? void 0 : request;
|
|
11844
13841
|
}
|
|
11845
13842
|
#busy() {
|
|
11846
13843
|
return this.#live.size > 0 || this.#calibrations.size > 0;
|
|
11847
13844
|
}
|
|
13845
|
+
#evaluate(context, host, advertised, names) {
|
|
13846
|
+
const request = this.#current();
|
|
13847
|
+
const { name, arguments: args } = context.call;
|
|
13848
|
+
if (request?.denied(name, args) === true) return {
|
|
13849
|
+
zone: "ledger",
|
|
13850
|
+
verdict: "deny",
|
|
13851
|
+
reason: LEDGER_NOTES.repeat
|
|
13852
|
+
};
|
|
13853
|
+
if (this.#enables(name)) {
|
|
13854
|
+
const admitted = (0, _orkestrel_contract.attempt)(() => request?.admit(name, args));
|
|
13855
|
+
if (!admitted.success) {
|
|
13856
|
+
if ((0, _orkestrel_contract.isError)(admitted.error) && admitted.error.message === LEDGER_NOTES.repeat) return {
|
|
13857
|
+
zone: "ledger",
|
|
13858
|
+
verdict: "deny",
|
|
13859
|
+
reason: LEDGER_NOTES.repeat
|
|
13860
|
+
};
|
|
13861
|
+
throw admitted.error;
|
|
13862
|
+
}
|
|
13863
|
+
if (request !== void 0) {
|
|
13864
|
+
const commitment = this.#room(request, name, args);
|
|
13865
|
+
if (commitment === void 0) return {
|
|
13866
|
+
zone: "ledger",
|
|
13867
|
+
verdict: "deny",
|
|
13868
|
+
reason: LEDGER_NOTES.overflow
|
|
13869
|
+
};
|
|
13870
|
+
request.commit(commitment.price, commitment.skill);
|
|
13871
|
+
}
|
|
13872
|
+
return host === void 0 ? advertised.evaluate(context) : host.evaluate(context);
|
|
13873
|
+
}
|
|
13874
|
+
const decision = host === void 0 || name === "recall" || !names.includes(name) ? advertised.evaluate(context) : host.evaluate(context);
|
|
13875
|
+
if (request !== void 0 && decision.verdict !== "deny" && name !== "recall" && names.includes(name)) request.commit(this.#priceResult(request.calls));
|
|
13876
|
+
return decision;
|
|
13877
|
+
}
|
|
13878
|
+
#room(request, name, args) {
|
|
13879
|
+
if (name !== "load" && name !== "remember" && name !== "switch") return { price: 0 };
|
|
13880
|
+
const gauge = this.#gauge;
|
|
13881
|
+
if (gauge === void 0) return void 0;
|
|
13882
|
+
const room = gauge.remainder(request.calls) - this.#predict;
|
|
13883
|
+
const admitted = (0, _orkestrel_contract.admitValue)(builtinToolContracts[name].contract, { ...args });
|
|
13884
|
+
const values = admitted.success ? admitted.value.value : {};
|
|
13885
|
+
if (name === "remember") {
|
|
13886
|
+
const line = (0, _orkestrel_contract.isString)(values["name"]) && (0, _orkestrel_contract.isString)(values["description"]) ? renderIndexEntry({
|
|
13887
|
+
name: values["name"],
|
|
13888
|
+
description: values["description"]
|
|
13889
|
+
}) : "";
|
|
13890
|
+
const price = gauge.scale * estimateTokens(line);
|
|
13891
|
+
return (request.committed + price) * 1.06 > room ? void 0 : { price };
|
|
13892
|
+
}
|
|
13893
|
+
const ceiling = this.#ceiling(request);
|
|
13894
|
+
const shown = this.#show(ceiling);
|
|
13895
|
+
if (name === "switch") {
|
|
13896
|
+
const workspace = values["workspace"];
|
|
13897
|
+
const price = (0, _orkestrel_contract.isString)(workspace) ? gauge.scale * Math.max(0, this.#estimateHead(request, ceiling, {
|
|
13898
|
+
skills: shown.skills,
|
|
13899
|
+
workspace
|
|
13900
|
+
}) - this.#estimateHead(request, ceiling, shown)) : 0;
|
|
13901
|
+
return (request.committed + price) * 1.06 > room ? void 0 : { price };
|
|
13902
|
+
}
|
|
13903
|
+
const thread = [.../* @__PURE__ */ new Set([...shown.skills, ...request.loads])];
|
|
13904
|
+
const skill = filterAllowList(ceiling.skills, this.#agent.context.skills.skills(), (one) => one.name).find((one) => one.name === values["skill"]);
|
|
13905
|
+
if (skill === void 0 || thread.includes(skill.name)) return { price: 0 };
|
|
13906
|
+
const price = this.#priceSkill(skill, ceiling, this.#advertise(ceiling, thread));
|
|
13907
|
+
const budget = (this.#options.capacity - this.#predict) * this.#share.prompt;
|
|
13908
|
+
return (request.committed + price + this.#priceResult(request.calls)) * 1.06 > room || this.#priceShown({ skills: thread }, ceiling) + price > this.#share.skills * budget ? void 0 : {
|
|
13909
|
+
price,
|
|
13910
|
+
skill: skill.name
|
|
13911
|
+
};
|
|
13912
|
+
}
|
|
13913
|
+
#estimateHead(request, ceiling, shown) {
|
|
13914
|
+
return estimateMessages(stripThinking(this.#agent.context.build({
|
|
13915
|
+
...request.selection === void 0 ? {} : { selection: request.selection },
|
|
13916
|
+
disclosure: shown,
|
|
13917
|
+
switchable: this.#advertise(ceiling, shown.skills).some((tool) => tool.name === "switch")
|
|
13918
|
+
}), this.#replay));
|
|
13919
|
+
}
|
|
13920
|
+
#reopen(call, result) {
|
|
13921
|
+
const request = this.#current();
|
|
13922
|
+
const entered = request?.message;
|
|
13923
|
+
if (request === void 0 || entered === void 0 || !result.success || !this.#enables(call.name)) return;
|
|
13924
|
+
const messages = this.#conversation.messages();
|
|
13925
|
+
const pairs = collectToolGroups(messages.slice(messages.findIndex((message) => message.id === entered.id) + 1)).flatMap((group) => group.slice(1).flatMap((message) => {
|
|
13926
|
+
const owner = findToolCall(group, message);
|
|
13927
|
+
return owner === void 0 ? [] : [{
|
|
13928
|
+
call: owner,
|
|
13929
|
+
content: message.content
|
|
13930
|
+
}];
|
|
13931
|
+
}));
|
|
13932
|
+
const key = (0, _orkestrel_contract.canonicalStringify)(call.arguments);
|
|
13933
|
+
const reopened = pairs.filter(({ call: earlier, content }) => call.name === "unload" ? earlier.name === "load" && earlier.arguments.skill === call.arguments.skill || content === settleToolResult(denyCall(earlier, LEDGER_NOTES.overflow)).content : call.name === "load" ? earlier.name === "unload" && earlier.arguments.skill === call.arguments.skill : call.name === "switch" ? earlier.name === "switch" && (0, _orkestrel_contract.canonicalStringify)(earlier.arguments) !== key : call.name === "remember" && earlier.name === "memo" && earlier.arguments.name === call.arguments.name);
|
|
13934
|
+
for (const { call: earlier } of reopened) request.release(earlier.name, earlier.arguments);
|
|
13935
|
+
}
|
|
11848
13936
|
#flush() {
|
|
11849
13937
|
if (this.#pending.size === 0) return;
|
|
11850
13938
|
const messages = this.#conversation.messages();
|
|
@@ -11895,7 +13983,7 @@ var Ledger = class {
|
|
|
11895
13983
|
for (const message of messages) {
|
|
11896
13984
|
if (!snapshot.failures.includes(message.id)) continue;
|
|
11897
13985
|
const group = groups.find((entries) => entries.some((one) => one.id === message.id));
|
|
11898
|
-
const call = group === void 0 ? void 0 :
|
|
13986
|
+
const call = group === void 0 ? void 0 : findToolCall(group, message);
|
|
11899
13987
|
this.#results.set(message.id, {
|
|
11900
13988
|
success: false,
|
|
11901
13989
|
id: call?.id ?? message.call ?? message.id,
|
|
@@ -11925,7 +14013,7 @@ var Ledger = class {
|
|
|
11925
14013
|
#readLookups(messages) {
|
|
11926
14014
|
this.#flush();
|
|
11927
14015
|
return collectToolGroups(messages).flatMap((group) => group.slice(1).flatMap((message) => {
|
|
11928
|
-
const call =
|
|
14016
|
+
const call = findToolCall(group, message);
|
|
11929
14017
|
const lookup = this.#options.lookups?.find((one) => one.tool.name === call?.name);
|
|
11930
14018
|
const write = this.#options.writes?.find((one) => one.tool.name === call?.name);
|
|
11931
14019
|
if (call === void 0 || lookup === void 0 && write === void 0 || this.#results.get(message.id)?.success === false) return [];
|
|
@@ -11992,7 +14080,7 @@ var Ledger = class {
|
|
|
11992
14080
|
const start = messages.findIndex((message) => message.id === request.id);
|
|
11993
14081
|
return messages.slice(start + 1).filter((message) => message.role !== "assistant" || hasCalls(message) || message.content.trim() !== "");
|
|
11994
14082
|
}
|
|
11995
|
-
async #select(conversation, message, signal) {
|
|
14083
|
+
async #select(conversation, message, signal, filter) {
|
|
11996
14084
|
const request = this.#current();
|
|
11997
14085
|
const selected = request?.message;
|
|
11998
14086
|
if (conversation !== this.#conversation || request === void 0 || selected === void 0 || message.id !== selected.id && !this.#annotations.has(message.id)) return this.#refuse(new LedgerError("REQUEST", "ledger selection requires a request owned by an active respond call"), { judgments: [] }, conversation);
|
|
@@ -12004,7 +14092,7 @@ var Ledger = class {
|
|
|
12004
14092
|
const scanned = (0, _orkestrel_contract.attempt)(() => this.#scan(this.#conversation.messages()));
|
|
12005
14093
|
if (scanned.success) this.#hold(scanned.value);
|
|
12006
14094
|
try {
|
|
12007
|
-
return await this.#classifier.classify(this.#requests, signal).then((classified) => this.#file(classified, request, selected, message), (error) => this.#refuse(error, { judgments: [] }, this.#conversation));
|
|
14095
|
+
return await this.#classifier.classify(this.#requests, signal).then((classified) => this.#file(classified, request, selected, message, filter ?? this.#live.get(request)), (error) => this.#refuse(error, { judgments: [] }, this.#conversation));
|
|
12008
14096
|
} finally {
|
|
12009
14097
|
const [scan] = this.#scans;
|
|
12010
14098
|
if (scan !== void 0) this.#scans.delete(scan);
|
|
@@ -12026,11 +14114,11 @@ var Ledger = class {
|
|
|
12026
14114
|
this.#hold(scan);
|
|
12027
14115
|
return scan;
|
|
12028
14116
|
}
|
|
12029
|
-
#file(classified, request, selected, message) {
|
|
14117
|
+
#file(classified, request, selected, message, filter) {
|
|
12030
14118
|
const { attempts, failure, ...filed } = classified;
|
|
12031
14119
|
const planned = (0, _orkestrel_contract.attempt)(() => {
|
|
12032
14120
|
if (filed.fault !== void 0) throw filed.fault;
|
|
12033
|
-
const { draft, ...plan } = this.#plan(selected, this.#rescan());
|
|
14121
|
+
const { draft, ...plan } = this.#plan(selected, this.#rescan(), filter);
|
|
12034
14122
|
return {
|
|
12035
14123
|
draft,
|
|
12036
14124
|
selection: {
|
|
@@ -12063,7 +14151,7 @@ var Ledger = class {
|
|
|
12063
14151
|
const category = this.#classifier.category(source);
|
|
12064
14152
|
return !(role === "user" && !this.#classifier.decisive(source)) && (category === "rule" || category === "correction");
|
|
12065
14153
|
}
|
|
12066
|
-
#plan(request, scan) {
|
|
14154
|
+
#plan(request, scan, filter) {
|
|
12067
14155
|
const { input, projection } = this.#project(scan);
|
|
12068
14156
|
const registry = scan.registry;
|
|
12069
14157
|
const links = linkOwners(input.findings, registry.owners);
|
|
@@ -12075,10 +14163,20 @@ var Ledger = class {
|
|
|
12075
14163
|
}),
|
|
12076
14164
|
topics: [...near]
|
|
12077
14165
|
});
|
|
12078
|
-
const
|
|
14166
|
+
const lists = intersectFilters(void 0, filter);
|
|
14167
|
+
const bound = lists.tools;
|
|
14168
|
+
const disclosure = this.#show(lists);
|
|
14169
|
+
const advertised = this.#advertise(lists, disclosure.skills);
|
|
14170
|
+
const calibrated = this.#measuredSet();
|
|
14171
|
+
const price = computeToolPrice(calibrated, advertised, this.#priced());
|
|
14172
|
+
const total = Math.max(0, (this.#options.capacity - this.#predict) * this.#share.prompt - (this.#priced().overhead - price.spared + price.added) - this.#holdback(lists, disclosure, advertised, [])) / (1 + LEDGER_SCALE_DRIFT);
|
|
12079
14173
|
const built = this.#agent.context.build({
|
|
12080
|
-
|
|
12081
|
-
|
|
14174
|
+
selection: {
|
|
14175
|
+
messages: [request],
|
|
14176
|
+
judgments: []
|
|
14177
|
+
},
|
|
14178
|
+
disclosure,
|
|
14179
|
+
switchable: advertised.some((tool) => tool.name === "switch")
|
|
12082
14180
|
});
|
|
12083
14181
|
const tail = this.#selectTail(request, total, input, scan);
|
|
12084
14182
|
const tailIds = new Set(tail.map((message) => message.id));
|
|
@@ -12187,23 +14285,24 @@ var Ledger = class {
|
|
|
12187
14285
|
...rung.window.flatMap((entry) => [entry.request, entry.reply]),
|
|
12188
14286
|
buildRequestMessage(built.at(-1) ?? request, rung.rendered.text)
|
|
12189
14287
|
], this.#replay)]);
|
|
12190
|
-
const names = this.#agent.context.scope?.tools;
|
|
12191
|
-
const definitions = this.#agent.context.tools.definitions();
|
|
12192
14288
|
const offered = rungs.map((rung) => hasHiddenRecall(input, request.id, this.#requests, this.#annotations, [...tail.slice(0, -1), ...rung.window.flatMap((entry) => [entry.request, entry.reply])], [...rung.window.flatMap((entry) => entry.lines), ...rung.rendered.lines]));
|
|
12193
14289
|
const longest = this.#findLongest();
|
|
12194
14290
|
const closed = probes.map((messages, at) => {
|
|
12195
|
-
const tools =
|
|
14291
|
+
const tools = advertised.filter((tool) => tool.name !== "recall" || offered[at]);
|
|
12196
14292
|
const calls = [{
|
|
12197
14293
|
estimate: estimateMessages(messages),
|
|
12198
14294
|
tools: tools.length,
|
|
12199
|
-
|
|
14295
|
+
...computeToolPrice(calibrated, tools, this.#priced())
|
|
12200
14296
|
}];
|
|
12201
14297
|
return offered[at] === true && this.#gauge !== void 0 && this.#gauge.remainder(calls) - this.#predict < 2 * this.#gauge.reserve(calls, longest);
|
|
12202
14298
|
});
|
|
12203
14299
|
const prices = probes.map((messages) => this.#price(messages));
|
|
12204
14300
|
const admitted = rungs.find((_rung, at) => (prices[at] ?? Infinity) <= total && (closed[2] === true || closed[at] === false)) ?? empty;
|
|
12205
|
-
|
|
12206
|
-
|
|
14301
|
+
const recall = offered[rungs.indexOf(admitted)] === true;
|
|
14302
|
+
const ceiling = intersectKeys(this.#agent.context.scope?.tools, bound);
|
|
14303
|
+
const admits = ceiling === void 0 || ceiling.includes("recall");
|
|
14304
|
+
if (!recall && admits) {
|
|
14305
|
+
const tools = this.#definitions().filter((tool) => tool.name !== "recall").map((tool) => tool.name);
|
|
12207
14306
|
const narrowed = this.#agent.context.scope?.narrow({ tools }) ?? createScope({
|
|
12208
14307
|
name: "ledger",
|
|
12209
14308
|
tools
|
|
@@ -12226,10 +14325,11 @@ var Ledger = class {
|
|
|
12226
14325
|
window: admitted.window
|
|
12227
14326
|
};
|
|
12228
14327
|
const shown = buildLedgerRenderContext(scan.messages, [...kept, ...admitted.rendered.lines], []).complete;
|
|
14328
|
+
const route = recall && admits;
|
|
12229
14329
|
return {
|
|
12230
14330
|
draft,
|
|
12231
14331
|
messages: [
|
|
12232
|
-
...tail.slice(0, -1).map((message) => message.role === "tool" ? this.#renderTailStub(message, scan, shown) : message),
|
|
14332
|
+
...tail.slice(0, -1).map((message) => message.role === "tool" ? this.#renderTailStub(message, scan, shown, route) : message),
|
|
12233
14333
|
...admitted.window.flatMap((entry) => [entry.request, entry.reply]),
|
|
12234
14334
|
copy
|
|
12235
14335
|
]
|
|
@@ -12317,7 +14417,7 @@ var Ledger = class {
|
|
|
12317
14417
|
return [{
|
|
12318
14418
|
leader,
|
|
12319
14419
|
kept: (leader.calls ?? []).flatMap((call) => {
|
|
12320
|
-
const message = group.slice(1).find((result) =>
|
|
14420
|
+
const message = group.slice(1).find((result) => findToolCall(group, result) === call);
|
|
12321
14421
|
const result = message === void 0 ? void 0 : this.#results.get(message.id);
|
|
12322
14422
|
return message === void 0 || ![...this.#options.lookups ?? [], ...this.#options.writes ?? []].some((entry) => entry.tool.name === call.name) || result?.success === false && result.error === LEDGER_NOTES.repeat ? [] : [{
|
|
12323
14423
|
call,
|
|
@@ -12362,16 +14462,16 @@ var Ledger = class {
|
|
|
12362
14462
|
amendments: evidence.amendments
|
|
12363
14463
|
};
|
|
12364
14464
|
}
|
|
12365
|
-
#renderTailStub(message, scan, shown) {
|
|
14465
|
+
#renderTailStub(message, scan, shown, offered = true) {
|
|
12366
14466
|
const group = scan.group(message.id);
|
|
12367
|
-
const call = group === void 0 ? void 0 :
|
|
14467
|
+
const call = group === void 0 ? void 0 : findToolCall(group, message);
|
|
12368
14468
|
const write = this.#options.writes?.some((one) => one.tool.name === call?.name) === true;
|
|
12369
14469
|
const finding = scan.finding(message.id);
|
|
12370
14470
|
const hidden = renderStub(call?.name ?? "tool", call?.arguments ?? {}, "hidden");
|
|
12371
14471
|
const visible = renderStub(call?.name ?? "tool", call?.arguments ?? {}, "shown");
|
|
12372
14472
|
return {
|
|
12373
14473
|
...message,
|
|
12374
|
-
content: renderStub(call?.name ?? "tool", call?.arguments ?? {}, this.#results.get(message.id)?.success === false ? "failed" : finding?.result === void 0 ? "empty" : shown === void 0 ? hidden.length > visible.length ? "hidden" : "shown" : scan.findings.some((other) => shown.has(other.id) && matchesLookup(finding, other)) ? "shown" : "hidden", write)
|
|
14474
|
+
content: renderStub(call?.name ?? "tool", call?.arguments ?? {}, this.#results.get(message.id)?.success === false ? "failed" : finding?.result === void 0 ? "empty" : shown === void 0 ? hidden.length > visible.length ? "hidden" : "shown" : scan.findings.some((other) => shown.has(other.id) && matchesLookup(finding, other)) ? "shown" : "hidden", write, offered)
|
|
12375
14475
|
};
|
|
12376
14476
|
}
|
|
12377
14477
|
#release(call, result) {
|
|
@@ -12402,26 +14502,71 @@ var Ledger = class {
|
|
|
12402
14502
|
}]);
|
|
12403
14503
|
for (const finding of findings) if ((edges.get(finding.id) ?? []).includes(id) && this.#options.lookups?.some((lookup) => lookup.tool.name === finding.name) === true) request.release(finding.name, finding.arguments);
|
|
12404
14504
|
}
|
|
12405
|
-
#
|
|
14505
|
+
#observeRequest(sent) {
|
|
12406
14506
|
this.#flush();
|
|
12407
14507
|
const request = this.#current();
|
|
12408
|
-
|
|
12409
|
-
|
|
12410
|
-
const additions = this.#conversation.messages().slice(boundary);
|
|
12411
|
-
const messages = this.#agent.context.build(request.selection);
|
|
12412
|
-
const names = this.#agent.context.scope?.tools;
|
|
12413
|
-
const tools = this.#agent.context.tools.definitions().filter((tool) => names === void 0 || names.includes(tool.name));
|
|
14508
|
+
if (request?.boundary === void 0) return;
|
|
14509
|
+
const tools = sent.tools ?? [];
|
|
12414
14510
|
request.observe({
|
|
12415
|
-
estimate: estimateMessages(
|
|
14511
|
+
estimate: estimateMessages(sent.messages),
|
|
12416
14512
|
tools: tools.length,
|
|
12417
|
-
|
|
14513
|
+
...computeToolPrice(this.#measuredSet(), tools, this.#priced())
|
|
12418
14514
|
}, this.#conversation.messages().length);
|
|
12419
14515
|
}
|
|
12420
|
-
#
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
14516
|
+
#priced() {
|
|
14517
|
+
return this.#gauge ?? {
|
|
14518
|
+
scale: 1,
|
|
14519
|
+
overhead: 0
|
|
14520
|
+
};
|
|
14521
|
+
}
|
|
14522
|
+
#measuredSet() {
|
|
14523
|
+
return this.#measured ?? this.#calibrated();
|
|
14524
|
+
}
|
|
14525
|
+
#digest() {
|
|
14526
|
+
return this.#measured === void 0 ? void 0 : digestLedgerTools(this.#measured);
|
|
14527
|
+
}
|
|
14528
|
+
#enables(name) {
|
|
14529
|
+
return this.#builtins.some((one) => one === name);
|
|
14530
|
+
}
|
|
14531
|
+
#definitions() {
|
|
14532
|
+
return [...this.#agent.context.tools.definitions().filter((tool) => !this.#enables(tool.name)), ...collectBuiltinDefinitions({ builtins: this.#builtins })];
|
|
14533
|
+
}
|
|
14534
|
+
#calibrated() {
|
|
14535
|
+
const definitions = this.#definitions();
|
|
14536
|
+
if (!this.#enables("load")) return definitions;
|
|
14537
|
+
const held = new Set(this.#agent.context.skills.skills().flatMap((skill) => skill.tools));
|
|
14538
|
+
return definitions.filter((tool) => !held.has(tool.name));
|
|
14539
|
+
}
|
|
14540
|
+
#show(ceiling) {
|
|
14541
|
+
return resolveDisclosure(this.#agent.context, this.#conversation, this.#given, ceiling, this.#builtins);
|
|
14542
|
+
}
|
|
14543
|
+
#advertise(ceiling, shown) {
|
|
14544
|
+
return filterAdvertisedTools(filterBuiltinCandidates(this.#definitions(), ceiling), ceiling.tools, this.#enables("load") ? this.#agent.context.skills.skills() : [], shown);
|
|
14545
|
+
}
|
|
14546
|
+
#ceiling(request) {
|
|
14547
|
+
return intersectFilters(this.#agent.context.scope, this.#live.get(request));
|
|
14548
|
+
}
|
|
14549
|
+
#priceSkill(skill, ceiling, advertised) {
|
|
14550
|
+
const sent = new Set(advertised.map((tool) => tool.name));
|
|
14551
|
+
return computeSkillPrice(skill, filterAllowList(ceiling.tools, this.#definitions().filter((tool) => skill.tools.includes(tool.name) && !sent.has(tool.name)), (tool) => tool.name), this.#measuredSet(), this.#priced());
|
|
14552
|
+
}
|
|
14553
|
+
#priceShown(shown, ceiling) {
|
|
14554
|
+
const skills = shown.skills.flatMap((name) => this.#agent.context.skills.skill(name) ?? []);
|
|
14555
|
+
const named = new Set(skills.flatMap((skill) => skill.tools));
|
|
14556
|
+
const definitions = filterAllowList(ceiling.tools, this.#definitions().filter((tool) => named.has(tool.name)), (tool) => tool.name);
|
|
14557
|
+
const calibrated = this.#measuredSet();
|
|
14558
|
+
const gauge = this.#priced();
|
|
14559
|
+
return skills.reduce((sum, skill) => sum + computeSkillPrice(skill, [], calibrated, gauge), 0) + computeToolPrice(calibrated, [...calibrated, ...definitions], gauge).added;
|
|
14560
|
+
}
|
|
14561
|
+
#priceResult(calls) {
|
|
14562
|
+
const longest = this.#conversation.messages().filter((message) => message.role === "tool").reduce((found, message) => found === void 0 || message.content.length > found.content.length ? message : found, void 0);
|
|
14563
|
+
const gauge = this.#gauge;
|
|
14564
|
+
return Math.max(this.#priced().scale * (longest === void 0 ? 0 : estimateMessages([longest])), gauge === void 0 ? 0 : gauge.reserve(calls, this.#findLongest()));
|
|
14565
|
+
}
|
|
14566
|
+
#holdback(ceiling, shown, advertised, calls) {
|
|
14567
|
+
if (!advertised.some((tool) => tool.name === "load")) return 0;
|
|
14568
|
+
const loadable = filterAllowList(ceiling.skills, this.#agent.context.skills.skills(), (skill) => skill.name).filter((skill) => !shown.skills.includes(skill.name));
|
|
14569
|
+
return loadable.length === 0 ? 0 : Math.max(...loadable.map((skill) => this.#priceSkill(skill, ceiling, advertised))) + this.#priceResult(calls);
|
|
12425
14570
|
}
|
|
12426
14571
|
#findLongest() {
|
|
12427
14572
|
return this.#conversation.messages().filter((message) => message.role === "assistant").reduce((longest, message) => message.content.length > longest.length ? message.content : longest, "");
|
|
@@ -12449,12 +14594,7 @@ var Ledger = class {
|
|
|
12449
14594
|
request?.recall();
|
|
12450
14595
|
const guidance = `an owner name, an id, or one of ${renderTopicNames(this.#options.topics)}`;
|
|
12451
14596
|
if (topic === "") throw new Error(`recall needs a topic: ${guidance}`);
|
|
12452
|
-
const rate = this.#
|
|
12453
|
-
const call = calls.at(-1);
|
|
12454
|
-
const carried = request === void 0 ? 0 : estimateMessages(stripThinking([...this.#agent.context.build(request.selection), ...this.#conversation.messages().slice(request.boundary)], this.#replay));
|
|
12455
|
-
const completion = (0, _orkestrel_contract.isFiniteNumber)(call?.prompt) ? (call.completion ?? 0) - (this.#replay === "none" ? call.thinking ?? 0 : 0) : 0;
|
|
12456
|
-
const extra = Math.max(0, (carried - (call?.estimate ?? carried)) * rate - completion);
|
|
12457
|
-
const drift = (0, _orkestrel_contract.isFiniteNumber)(call?.prompt) ? 0 : Math.max(0, (call?.estimate ?? 0) * (rate - (gauge?.scale ?? rate)));
|
|
14597
|
+
const { rate, extra, slack: drift } = this.#carry(request, calls);
|
|
12458
14598
|
const siblings = estimateMessages((request?.listings ?? []).filter((listing) => listing.turn === calls.length).map((listing) => ({
|
|
12459
14599
|
id: "result",
|
|
12460
14600
|
role: "tool",
|
|
@@ -12529,7 +14669,7 @@ var Ledger = class {
|
|
|
12529
14669
|
this.#flush();
|
|
12530
14670
|
const groups = [...new Set(this.#collectAfter(entered).flatMap((message) => {
|
|
12531
14671
|
const result = this.#results.get(message.id);
|
|
12532
|
-
if (message.role !== "tool" || result?.success !== true) return [];
|
|
14672
|
+
if (message.role !== "tool" || result?.success !== true || result.name !== "memo" && this.#enables(result.name)) return [];
|
|
12533
14673
|
const recalled = result.name === "recall";
|
|
12534
14674
|
if (recalled && /^nothing earlier on /.test(message.content)) return [];
|
|
12535
14675
|
const listing = recalled ? request.listings.findLast((one) => one.result === message.content) : void 0;
|
|
@@ -12538,7 +14678,7 @@ var Ledger = class {
|
|
|
12538
14678
|
const unique = groups.filter((group, at) => !splitResultLines(group).every((line) => groups.slice(0, at).some((earlier) => splitResultLines(earlier).includes(line))));
|
|
12539
14679
|
const digest = unique.length === 0 ? void 0 : `${LEDGER_NOTES.results}\n${unique.join("\n")}`;
|
|
12540
14680
|
const omitted = /* @__PURE__ */ new Set();
|
|
12541
|
-
const rate = this.#
|
|
14681
|
+
const rate = this.#capacityRate(request) * (1 + LEDGER_SCALE_DRIFT);
|
|
12542
14682
|
const allowance = (this.#options.capacity - this.#predict) / rate;
|
|
12543
14683
|
const after = this.#collectAfter(entered);
|
|
12544
14684
|
const messages = [
|
|
@@ -12555,28 +14695,105 @@ var Ledger = class {
|
|
|
12555
14695
|
role: "user",
|
|
12556
14696
|
content: digest
|
|
12557
14697
|
}];
|
|
14698
|
+
const shown = this.#show(this.#ceiling(request));
|
|
12558
14699
|
if (this.#gauge === void 0 || estimateMessages(stripThinking(this.#agent.context.build({
|
|
12559
|
-
|
|
12560
|
-
|
|
14700
|
+
selection: {
|
|
14701
|
+
messages: [...messages, ...note],
|
|
14702
|
+
judgments: []
|
|
14703
|
+
},
|
|
14704
|
+
disclosure: shown
|
|
12561
14705
|
}), this.#replay)) <= allowance) return {
|
|
12562
14706
|
digest,
|
|
12563
14707
|
omitted
|
|
12564
14708
|
};
|
|
12565
14709
|
const partials = new Set(after.filter((message) => message.role === "assistant" && !hasCalls(message)).map((message) => message.id));
|
|
12566
|
-
const
|
|
12567
|
-
|
|
12568
|
-
|
|
12569
|
-
|
|
14710
|
+
const base = estimateMessages(stripThinking(this.#agent.context.build({
|
|
14711
|
+
selection: {
|
|
14712
|
+
messages: messages.filter((message) => !partials.has(message.id)),
|
|
14713
|
+
judgments: []
|
|
14714
|
+
},
|
|
14715
|
+
disclosure: shown
|
|
14716
|
+
}), this.#replay));
|
|
14717
|
+
if (base > allowance) throw new LedgerError("OVERFLOW", `the answer pass prices ${Math.ceil(base * rate)} tokens with every cut made, over the capacity ${this.#options.capacity} less predict ${this.#predict}; shorten the request, narrow what the scope shows, or raise capacity`, { context: {
|
|
14718
|
+
capacity: this.#options.capacity,
|
|
14719
|
+
predict: this.#predict,
|
|
14720
|
+
price: base * rate
|
|
14721
|
+
} });
|
|
14722
|
+
const fitted = cutListing(unique, allowance - base - estimateTokens(`${LEDGER_NOTES.results}\n`), "more", false);
|
|
14723
|
+
const cut = fitted === "" ? void 0 : `${LEDGER_NOTES.results}\n${fitted}`;
|
|
12570
14724
|
return {
|
|
12571
|
-
digest:
|
|
14725
|
+
digest: cut !== void 0 && base + estimateMessages([{
|
|
14726
|
+
id: "results",
|
|
14727
|
+
role: "user",
|
|
14728
|
+
content: cut
|
|
14729
|
+
}]) > allowance ? void 0 : cut,
|
|
12572
14730
|
omitted: partials
|
|
12573
14731
|
};
|
|
12574
14732
|
}
|
|
14733
|
+
#carry(request, calls) {
|
|
14734
|
+
const rate = this.#recallRate(request) * (1 + LEDGER_SCALE_DRIFT);
|
|
14735
|
+
const ceiling = request === void 0 ? {} : this.#ceiling(request);
|
|
14736
|
+
const shown = this.#show(ceiling);
|
|
14737
|
+
const carried = request === void 0 ? 0 : estimateMessages(stripThinking([...this.#agent.context.build({
|
|
14738
|
+
...request.selection === void 0 ? {} : { selection: request.selection },
|
|
14739
|
+
disclosure: shown,
|
|
14740
|
+
switchable: this.#advertise(ceiling, shown.skills).some((tool) => tool.name === "switch")
|
|
14741
|
+
}), ...this.#conversation.messages().slice(request.boundary)], this.#replay));
|
|
14742
|
+
return {
|
|
14743
|
+
rate,
|
|
14744
|
+
...this.#terms(calls.at(-1), carried, rate)
|
|
14745
|
+
};
|
|
14746
|
+
}
|
|
14747
|
+
#terms(call, carried, rate) {
|
|
14748
|
+
const reply = (0, _orkestrel_contract.isFiniteNumber)(call?.prompt) ? (call.completion ?? 0) - (this.#replay === "none" ? call.thinking ?? 0 : 0) : 0;
|
|
14749
|
+
return {
|
|
14750
|
+
extra: Math.max(0, (carried - (call?.estimate ?? carried)) * rate - reply),
|
|
14751
|
+
slack: (0, _orkestrel_contract.isFiniteNumber)(call?.prompt) ? 0 : Math.max(0, (call?.estimate ?? 0) * (rate - (this.#gauge?.scale ?? rate)))
|
|
14752
|
+
};
|
|
14753
|
+
}
|
|
14754
|
+
#gate(index, sent) {
|
|
14755
|
+
const request = this.#current();
|
|
14756
|
+
if (request?.boundary === void 0) return true;
|
|
14757
|
+
const left = (0, _orkestrel_contract.attempt)(() => index === 0 ? this.#first(request, sent) : this.#next(request, sent));
|
|
14758
|
+
if (left.success && left.value >= 0) return true;
|
|
14759
|
+
const limit = this.#options.capacity - this.#predict;
|
|
14760
|
+
this.#refusal = new LedgerError("OVERFLOW", left.success ? `the request prices ${Math.ceil(limit - left.value)} tokens, over the capacity ${this.#options.capacity} less predict ${this.#predict}; shorten the request, narrow what the scope shows, or raise capacity` : `the request could not be priced against the capacity ${this.#options.capacity} less predict ${this.#predict}`, { context: {
|
|
14761
|
+
capacity: this.#options.capacity,
|
|
14762
|
+
predict: this.#predict,
|
|
14763
|
+
price: left.success ? limit - left.value : Number.POSITIVE_INFINITY
|
|
14764
|
+
} });
|
|
14765
|
+
this.#agent.abort("overflow");
|
|
14766
|
+
return false;
|
|
14767
|
+
}
|
|
14768
|
+
#first(request, sent) {
|
|
14769
|
+
const gauge = this.#gauge;
|
|
14770
|
+
if (gauge === void 0) throw new LedgerError("GAUGE", "the gate needs a gauge");
|
|
14771
|
+
const tools = computeToolPrice(this.#measuredSet(), sent.tools ?? [], gauge);
|
|
14772
|
+
const rate = request.calls.length === 0 ? gauge.scale : this.#capacityRate(request);
|
|
14773
|
+
return this.#options.capacity - this.#predict - (Math.max(0, gauge.overhead - tools.spared) + tools.added + rate * (1 + LEDGER_SCALE_DRIFT) * estimateMessages(sent.messages));
|
|
14774
|
+
}
|
|
14775
|
+
#next(request, sent) {
|
|
14776
|
+
request.think(this.#conversation.messages());
|
|
14777
|
+
const gauge = this.#gauge;
|
|
14778
|
+
if (gauge === void 0) throw new LedgerError("GAUGE", "the gate needs a gauge");
|
|
14779
|
+
const calls = request.calls;
|
|
14780
|
+
const last = calls.at(-1);
|
|
14781
|
+
if (last === void 0) throw new Error("a later request has no recorded call before it");
|
|
14782
|
+
const measured = this.#measuredSet();
|
|
14783
|
+
const rate = this.#capacityRate(request) * (1 + LEDGER_SCALE_DRIFT);
|
|
14784
|
+
const { extra, slack } = this.#terms(last, estimateMessages(sent.messages), rate);
|
|
14785
|
+
const tools = computeToolPrice(measured, sent.tools ?? [], gauge);
|
|
14786
|
+
const grown = (Math.min(gauge.overhead, Math.max(0, (last.spared ?? 0) - tools.spared) * computeToolDensity(measured, gauge) / gauge.scale) + Math.max(0, tools.added - (last.added ?? 0))) * (1 + LEDGER_SCALE_DRIFT);
|
|
14787
|
+
return gauge.remainder(calls) - this.#predict - extra - slack - grown;
|
|
14788
|
+
}
|
|
14789
|
+
#capacityRate(request) {
|
|
14790
|
+
return Math.max(this.#recallRate(request), this.#gauge?.scale ?? 1);
|
|
14791
|
+
}
|
|
12575
14792
|
#recallRate(request) {
|
|
12576
14793
|
const gauge = this.#gauge;
|
|
12577
14794
|
if (gauge === void 0) return 1;
|
|
12578
14795
|
const calls = request?.calls ?? [];
|
|
12579
|
-
const last = calls.findLast((call) => (0, _orkestrel_contract.isFiniteNumber)(call.prompt));
|
|
14796
|
+
const last = calls.findLast((call) => (0, _orkestrel_contract.isFiniteNumber)(call.prompt) && (call.added ?? 0) === 0);
|
|
12580
14797
|
const measured = last !== void 0 && (0, _orkestrel_contract.isFiniteNumber)(last.prompt) && last.estimate > 0 && ((last.spared ?? 0) === 0 || (last.spared ?? 0) >= gauge.overhead) ? (last.prompt - Math.max(0, gauge.overhead - (last.spared ?? 0))) / last.estimate : 0;
|
|
12581
14798
|
return Math.max(gauge.rate(calls), measured);
|
|
12582
14799
|
}
|
|
@@ -12597,7 +14814,12 @@ var Ledger = class {
|
|
|
12597
14814
|
function createLedger(provider, options) {
|
|
12598
14815
|
return new Ledger(provider, options);
|
|
12599
14816
|
}
|
|
14817
|
+
exports.AGENT_OPTION_KEYS = AGENT_OPTION_KEYS;
|
|
14818
|
+
exports.AGENT_RUN_KEYS = AGENT_RUN_KEYS;
|
|
14819
|
+
exports.ARGUMENT_BLANK_PATTERN = ARGUMENT_BLANK_PATTERN;
|
|
14820
|
+
exports.ARGUMENT_CONTROL_PATTERN = ARGUMENT_CONTROL_PATTERN;
|
|
12600
14821
|
exports.AUTHORITY_NOTES = AUTHORITY_NOTES;
|
|
14822
|
+
exports.AUTHORITY_OPTION_KEYS = AUTHORITY_OPTION_KEYS;
|
|
12601
14823
|
exports.Agent = Agent;
|
|
12602
14824
|
exports.AgentContext = AgentContext;
|
|
12603
14825
|
exports.AgentError = AgentError;
|
|
@@ -12608,6 +14830,16 @@ exports.AgentProvider = AgentProvider;
|
|
|
12608
14830
|
exports.AgentRegistry = AgentRegistry;
|
|
12609
14831
|
exports.Approver = Approver;
|
|
12610
14832
|
exports.Authority = Authority;
|
|
14833
|
+
exports.BUILD_OPTION_KEYS = BUILD_OPTION_KEYS;
|
|
14834
|
+
exports.BUILTIN_LIMITS = BUILTIN_LIMITS;
|
|
14835
|
+
exports.BUILTIN_NOTES = BUILTIN_NOTES;
|
|
14836
|
+
exports.BUILTIN_TOOLS = BUILTIN_TOOLS;
|
|
14837
|
+
exports.CLASSIFIER_OPTION_KEYS = CLASSIFIER_OPTION_KEYS;
|
|
14838
|
+
exports.COMPACT_OPTION_KEYS = COMPACT_OPTION_KEYS;
|
|
14839
|
+
exports.CONTEXT_OPTION_KEYS = CONTEXT_OPTION_KEYS;
|
|
14840
|
+
exports.CONVERSATION_INPUT_KEYS = CONVERSATION_INPUT_KEYS;
|
|
14841
|
+
exports.CONVERSATION_MANAGER_KEYS = CONVERSATION_MANAGER_KEYS;
|
|
14842
|
+
exports.CONVERSATION_OPTION_KEYS = CONVERSATION_OPTION_KEYS;
|
|
12611
14843
|
exports.CONVERSATION_RECAP_PREFIX = CONVERSATION_RECAP_PREFIX;
|
|
12612
14844
|
exports.CONVERSATION_ROLES = CONVERSATION_ROLES;
|
|
12613
14845
|
exports.Channel = Channel;
|
|
@@ -12627,27 +14859,36 @@ exports.DEFAULT_RELAY_LIMIT = DEFAULT_RELAY_LIMIT;
|
|
|
12627
14859
|
exports.DETERMINISTIC_JUDGE_ERROR = DETERMINISTIC_JUDGE_ERROR;
|
|
12628
14860
|
exports.DatabaseConversationStore = DatabaseConversationStore;
|
|
12629
14861
|
exports.DatabaseMemoStore = DatabaseMemoStore;
|
|
14862
|
+
exports.GAUGE_OPTION_KEYS = GAUGE_OPTION_KEYS;
|
|
12630
14863
|
exports.Gauge = Gauge;
|
|
12631
14864
|
exports.IMAGE_TOKEN_ESTIMATE = IMAGE_TOKEN_ESTIMATE;
|
|
14865
|
+
exports.INSTRUCTION_MANAGER_KEYS = INSTRUCTION_MANAGER_KEYS;
|
|
12632
14866
|
exports.INVALID_RELAY_STATUS = INVALID_RELAY_STATUS;
|
|
12633
14867
|
exports.Instruction = Instruction;
|
|
12634
14868
|
exports.InstructionManager = InstructionManager;
|
|
14869
|
+
exports.JOB_REFUSAL_PREFIX = JOB_REFUSAL_PREFIX;
|
|
14870
|
+
exports.JUDGE_INPUT_KEYS = JUDGE_INPUT_KEYS;
|
|
12635
14871
|
exports.JudgeAbortError = JudgeAbortError;
|
|
12636
14872
|
exports.JudgeError = JudgeError;
|
|
12637
14873
|
exports.JudgmentManager = JudgmentManager;
|
|
14874
|
+
exports.LEDGER_AGENT_KEYS = LEDGER_AGENT_KEYS;
|
|
12638
14875
|
exports.LEDGER_CATEGORIES = LEDGER_CATEGORIES;
|
|
12639
14876
|
exports.LEDGER_NOTES = LEDGER_NOTES;
|
|
14877
|
+
exports.LEDGER_OPTION_KEYS = LEDGER_OPTION_KEYS;
|
|
12640
14878
|
exports.LEDGER_OWNER_PREFIX = LEDGER_OWNER_PREFIX;
|
|
12641
14879
|
exports.LEDGER_QUESTIONS = LEDGER_QUESTIONS;
|
|
12642
14880
|
exports.LEDGER_RECALL_LABELS = LEDGER_RECALL_LABELS;
|
|
12643
14881
|
exports.LEDGER_RULES_KEY = LEDGER_RULES_KEY;
|
|
14882
|
+
exports.LEDGER_SAMPLE_BYTES = LEDGER_SAMPLE_BYTES;
|
|
12644
14883
|
exports.LEDGER_SCALE_DRIFT = LEDGER_SCALE_DRIFT;
|
|
12645
14884
|
exports.LEDGER_SLICE = LEDGER_SLICE;
|
|
12646
14885
|
exports.LEDGER_WORD = LEDGER_WORD;
|
|
14886
|
+
exports.LOADED_SECTION_HEADER = LOADED_SECTION_HEADER;
|
|
12647
14887
|
exports.Ledger = Ledger;
|
|
12648
14888
|
exports.LedgerError = LedgerError;
|
|
12649
14889
|
exports.LedgerRequest = LedgerRequest;
|
|
12650
14890
|
exports.MAX_ERROR_BODY_LENGTH = MAX_ERROR_BODY_LENGTH;
|
|
14891
|
+
exports.MEMO_MANAGER_KEYS = MEMO_MANAGER_KEYS;
|
|
12651
14892
|
exports.MEMO_SECTION_HEADER = MEMO_SECTION_HEADER;
|
|
12652
14893
|
exports.MESSAGE_ROLES = MESSAGE_ROLES;
|
|
12653
14894
|
exports.MESSAGE_TOKEN_OVERHEAD = MESSAGE_TOKEN_OVERHEAD;
|
|
@@ -12656,17 +14897,31 @@ exports.MemoryConversationStore = MemoryConversationStore;
|
|
|
12656
14897
|
exports.MemoryMemoStore = MemoryMemoStore;
|
|
12657
14898
|
exports.OVERSIZED_RELAY_STATUS = OVERSIZED_RELAY_STATUS;
|
|
12658
14899
|
exports.PLACED_CATEGORIES = PLACED_CATEGORIES;
|
|
14900
|
+
exports.PROVIDER_INPUT_KEYS = PROVIDER_INPUT_KEYS;
|
|
12659
14901
|
exports.ProviderAbortError = ProviderAbortError;
|
|
12660
14902
|
exports.ProviderError = ProviderError;
|
|
14903
|
+
exports.QUEUE_OPTION_KEYS = QUEUE_OPTION_KEYS;
|
|
12661
14904
|
exports.QUIET_CATEGORIES = QUIET_CATEGORIES;
|
|
14905
|
+
exports.REFERENCE_OPTION_KEYS = REFERENCE_OPTION_KEYS;
|
|
12662
14906
|
exports.REFUSED_RELAY_CONTENT_TYPE = REFUSED_RELAY_CONTENT_TYPE;
|
|
12663
14907
|
exports.REFUSED_RELAY_STATUS = REFUSED_RELAY_STATUS;
|
|
14908
|
+
exports.REGISTRY_OPTION_KEYS = REGISTRY_OPTION_KEYS;
|
|
12664
14909
|
exports.RELAY_CONTENT_TYPE = RELAY_CONTENT_TYPE;
|
|
14910
|
+
exports.RELAY_OPTION_KEYS = RELAY_OPTION_KEYS;
|
|
14911
|
+
exports.RELAY_PROVIDER_KEYS = RELAY_PROVIDER_KEYS;
|
|
12665
14912
|
exports.RELAY_PROVIDER_MESSAGE = RELAY_PROVIDER_MESSAGE;
|
|
14913
|
+
exports.RELAY_STREAM_KEYS = RELAY_STREAM_KEYS;
|
|
14914
|
+
exports.RUNNER_OPTION_KEYS = RUNNER_OPTION_KEYS;
|
|
12666
14915
|
exports.RelayProvider = RelayProvider;
|
|
12667
14916
|
exports.RelayStream = RelayStream;
|
|
14917
|
+
exports.SCOPE_FILTER_KEYS = SCOPE_FILTER_KEYS;
|
|
14918
|
+
exports.SCOPE_KEYS = SCOPE_KEYS;
|
|
14919
|
+
exports.SCOPE_MANAGER_KEYS = SCOPE_MANAGER_KEYS;
|
|
12668
14920
|
exports.SCOPE_NOTE = SCOPE_NOTE;
|
|
14921
|
+
exports.SKILL_MANAGER_KEYS = SKILL_MANAGER_KEYS;
|
|
12669
14922
|
exports.SKILL_SECTION_HEADER = SKILL_SECTION_HEADER;
|
|
14923
|
+
exports.STREAM_OPTION_KEYS = STREAM_OPTION_KEYS;
|
|
14924
|
+
exports.SYSTEM_ONE_OPTION_KEYS = SYSTEM_ONE_OPTION_KEYS;
|
|
12670
14925
|
exports.SYSTEM_ONE_PATH = SYSTEM_ONE_PATH;
|
|
12671
14926
|
exports.Scope = Scope;
|
|
12672
14927
|
exports.ScopeManager = ScopeManager;
|
|
@@ -12677,6 +14932,7 @@ exports.THINK_OPEN = THINK_OPEN;
|
|
|
12677
14932
|
exports.ThinkSplitter = ThinkSplitter;
|
|
12678
14933
|
exports.UNAUTHORIZED_RELAY_STATUS = UNAUTHORIZED_RELAY_STATUS;
|
|
12679
14934
|
exports.UPSTREAM_RELAY_STATUS = UPSTREAM_RELAY_STATUS;
|
|
14935
|
+
exports.WORKSPACE_INDEX_HEADER = WORKSPACE_INDEX_HEADER;
|
|
12680
14936
|
exports.WORKSPACE_SECTION_HEADER = WORKSPACE_SECTION_HEADER;
|
|
12681
14937
|
exports.agentAnswerContract = agentAnswerContract;
|
|
12682
14938
|
exports.agentAnswerShape = agentAnswerShape;
|
|
@@ -12709,10 +14965,14 @@ exports.buildRecords = buildRecords;
|
|
|
12709
14965
|
exports.buildRelayRequest = buildRelayRequest;
|
|
12710
14966
|
exports.buildRequestMessage = buildRequestMessage;
|
|
12711
14967
|
exports.buildSummaryMessage = buildSummaryMessage;
|
|
14968
|
+
exports.builtinToolContracts = builtinToolContracts;
|
|
12712
14969
|
exports.chargeUsage = chargeUsage;
|
|
12713
14970
|
exports.collectAmendments = collectAmendments;
|
|
14971
|
+
exports.collectBuiltinDefinitions = collectBuiltinDefinitions;
|
|
14972
|
+
exports.collectDisclosure = collectDisclosure;
|
|
12714
14973
|
exports.collectExchanges = collectExchanges;
|
|
12715
14974
|
exports.collectImageData = collectImageData;
|
|
14975
|
+
exports.collectJobRefusals = collectJobRefusals;
|
|
12716
14976
|
exports.collectLedgerEvidence = collectLedgerEvidence;
|
|
12717
14977
|
exports.collectLedgerLines = collectLedgerLines;
|
|
12718
14978
|
exports.collectLedgerReplies = collectLedgerReplies;
|
|
@@ -12725,7 +14985,10 @@ exports.collectRetiredFindings = collectRetiredFindings;
|
|
|
12725
14985
|
exports.collectToolGroups = collectToolGroups;
|
|
12726
14986
|
exports.collectWriteSupersessions = collectWriteSupersessions;
|
|
12727
14987
|
exports.computeLedgerChecksum = computeLedgerChecksum;
|
|
14988
|
+
exports.computeSkillPrice = computeSkillPrice;
|
|
12728
14989
|
exports.computeThinking = computeThinking;
|
|
14990
|
+
exports.computeToolDensity = computeToolDensity;
|
|
14991
|
+
exports.computeToolPrice = computeToolPrice;
|
|
12729
14992
|
exports.conversationSnapshotContract = conversationSnapshotContract;
|
|
12730
14993
|
exports.conversationSnapshotShape = conversationSnapshotShape;
|
|
12731
14994
|
exports.copyJSON = copyJSON;
|
|
@@ -12736,6 +14999,7 @@ exports.createAgentRegistry = createAgentRegistry;
|
|
|
12736
14999
|
exports.createAgentRunner = createAgentRunner;
|
|
12737
15000
|
exports.createApprover = createApprover;
|
|
12738
15001
|
exports.createAuthority = createAuthority;
|
|
15002
|
+
exports.createBuiltinTool = createBuiltinTool;
|
|
12739
15003
|
exports.createChannel = createChannel;
|
|
12740
15004
|
exports.createConversation = createConversation;
|
|
12741
15005
|
exports.createConversationManager = createConversationManager;
|
|
@@ -12755,7 +15019,9 @@ exports.createSkillManager = createSkillManager;
|
|
|
12755
15019
|
exports.createSystemOneJudge = createSystemOneJudge;
|
|
12756
15020
|
exports.createThinkSplitter = createThinkSplitter;
|
|
12757
15021
|
exports.cutListing = cutListing;
|
|
15022
|
+
exports.cutWorkspaceSample = cutWorkspaceSample;
|
|
12758
15023
|
exports.denyCall = denyCall;
|
|
15024
|
+
exports.describeCandidates = describeCandidates;
|
|
12759
15025
|
exports.describeQuestionRefusal = describeQuestionRefusal;
|
|
12760
15026
|
exports.describeRelayRefusal = describeRelayRefusal;
|
|
12761
15027
|
exports.describeSnapshotRefusal = describeSnapshotRefusal;
|
|
@@ -12769,9 +15035,11 @@ exports.extractQueueOptions = extractQueueOptions;
|
|
|
12769
15035
|
exports.extractSystemOneAnswer = extractSystemOneAnswer;
|
|
12770
15036
|
exports.extractSystemOneUsage = extractSystemOneUsage;
|
|
12771
15037
|
exports.extractWords = extractWords;
|
|
15038
|
+
exports.filterAdvertisedTools = filterAdvertisedTools;
|
|
12772
15039
|
exports.filterAllowList = filterAllowList;
|
|
12773
|
-
exports.
|
|
15040
|
+
exports.filterBuiltinCandidates = filterBuiltinCandidates;
|
|
12774
15041
|
exports.findLedgerCut = findLedgerCut;
|
|
15042
|
+
exports.findToolCall = findToolCall;
|
|
12775
15043
|
exports.fitSlope = fitSlope;
|
|
12776
15044
|
exports.freezeJSON = freezeJSON;
|
|
12777
15045
|
exports.handleAgentQueueJob = handleAgentQueueJob;
|
|
@@ -12779,7 +15047,12 @@ exports.hasCalls = hasCalls;
|
|
|
12779
15047
|
exports.hasHiddenRecall = hasHiddenRecall;
|
|
12780
15048
|
exports.identifyLookup = identifyLookup;
|
|
12781
15049
|
exports.inferStop = inferStop;
|
|
15050
|
+
exports.instructionInputContract = instructionInputContract;
|
|
15051
|
+
exports.instructionInputShape = instructionInputShape;
|
|
15052
|
+
exports.intersectFilters = intersectFilters;
|
|
12782
15053
|
exports.intersectKeys = intersectKeys;
|
|
15054
|
+
exports.isAgentBuiltin = isAgentBuiltin;
|
|
15055
|
+
exports.isAgentCaller = isAgentCaller;
|
|
12783
15056
|
exports.isAgentError = isAgentError;
|
|
12784
15057
|
exports.isAgentJobError = isAgentJobError;
|
|
12785
15058
|
exports.isAuthorityDecision = isAuthorityDecision;
|
|
@@ -12809,6 +15082,7 @@ exports.isPositiveSafeInteger = isPositiveSafeInteger;
|
|
|
12809
15082
|
exports.isProviderAbortError = isProviderAbortError;
|
|
12810
15083
|
exports.isProviderError = isProviderError;
|
|
12811
15084
|
exports.isProviderResult = isProviderResult;
|
|
15085
|
+
exports.isScope = isScope;
|
|
12812
15086
|
exports.isSection = isSection;
|
|
12813
15087
|
exports.isSystemOneAnswer = isSystemOneAnswer;
|
|
12814
15088
|
exports.isSystemOneResponse = isSystemOneResponse;
|
|
@@ -12825,18 +15099,26 @@ exports.ledgerSnapshotShape = ledgerSnapshotShape;
|
|
|
12825
15099
|
exports.ledgerWindowContract = ledgerWindowContract;
|
|
12826
15100
|
exports.ledgerWindowShape = ledgerWindowShape;
|
|
12827
15101
|
exports.linkOwners = linkOwners;
|
|
15102
|
+
exports.loadToolContract = loadToolContract;
|
|
15103
|
+
exports.loadToolShape = loadToolShape;
|
|
12828
15104
|
exports.matchEntities = matchEntities;
|
|
12829
15105
|
exports.matchesCutLine = matchesCutLine;
|
|
15106
|
+
exports.matchesHeadLists = matchesHeadLists;
|
|
12830
15107
|
exports.matchesJSON = matchesJSON;
|
|
12831
15108
|
exports.matchesJudgment = matchesJudgment;
|
|
12832
15109
|
exports.matchesLookup = matchesLookup;
|
|
15110
|
+
exports.matchesPlainLine = matchesPlainLine;
|
|
12833
15111
|
exports.measureAnswer = measureAnswer;
|
|
12834
15112
|
exports.measureMessage = measureMessage;
|
|
12835
15113
|
exports.measureRequest = measureRequest;
|
|
12836
15114
|
exports.memoContract = memoContract;
|
|
15115
|
+
exports.memoInputContract = memoInputContract;
|
|
15116
|
+
exports.memoInputShape = memoInputShape;
|
|
12837
15117
|
exports.memoShape = memoShape;
|
|
12838
15118
|
exports.memoSnapshotContract = memoSnapshotContract;
|
|
12839
15119
|
exports.memoSnapshotShape = memoSnapshotShape;
|
|
15120
|
+
exports.memoToolContract = memoToolContract;
|
|
15121
|
+
exports.memoToolShape = memoToolShape;
|
|
12840
15122
|
exports.mergeSupersessions = mergeSupersessions;
|
|
12841
15123
|
exports.messageContract = messageContract;
|
|
12842
15124
|
exports.messageInputContract = messageInputContract;
|
|
@@ -12860,23 +15142,28 @@ exports.readProviderDelta = readProviderDelta;
|
|
|
12860
15142
|
exports.relayFrameContract = relayFrameContract;
|
|
12861
15143
|
exports.relayFrameShape = relayFrameShape;
|
|
12862
15144
|
exports.releaseReader = releaseReader;
|
|
15145
|
+
exports.rememberToolContract = rememberToolContract;
|
|
15146
|
+
exports.rememberToolShape = rememberToolShape;
|
|
12863
15147
|
exports.removeEntries = removeEntries;
|
|
12864
15148
|
exports.renderArguments = renderArguments;
|
|
12865
15149
|
exports.renderCauseChain = renderCauseChain;
|
|
12866
15150
|
exports.renderFencedFile = renderFencedFile;
|
|
12867
15151
|
exports.renderIndexEntry = renderIndexEntry;
|
|
15152
|
+
exports.renderIndexLine = renderIndexLine;
|
|
12868
15153
|
exports.renderJSON = renderJSON;
|
|
12869
15154
|
exports.renderLedgerAnnotation = renderLedgerAnnotation;
|
|
12870
15155
|
exports.renderLedgerPinned = renderLedgerPinned;
|
|
12871
15156
|
exports.renderLedgerRecord = renderLedgerRecord;
|
|
12872
15157
|
exports.renderLookupCall = renderLookupCall;
|
|
12873
15158
|
exports.renderSection = renderSection;
|
|
15159
|
+
exports.renderSkillBody = renderSkillBody;
|
|
12874
15160
|
exports.renderStub = renderStub;
|
|
12875
15161
|
exports.renderToolValue = renderToolValue;
|
|
12876
15162
|
exports.renderTopicNames = renderTopicNames;
|
|
12877
15163
|
exports.requestToRelay = requestToRelay;
|
|
12878
15164
|
exports.requireAnswer = requireAnswer;
|
|
12879
15165
|
exports.requireBudget = requireBudget;
|
|
15166
|
+
exports.requireBuiltins = requireBuiltins;
|
|
12880
15167
|
exports.requireConversation = requireConversation;
|
|
12881
15168
|
exports.requireConversationSnapshot = requireConversationSnapshot;
|
|
12882
15169
|
exports.requireCumulative = requireCumulative;
|
|
@@ -12888,15 +15175,21 @@ exports.requireKeep = requireKeep;
|
|
|
12888
15175
|
exports.requireLimit = requireLimit;
|
|
12889
15176
|
exports.requireMessageInput = requireMessageInput;
|
|
12890
15177
|
exports.requireProviderResult = requireProviderResult;
|
|
15178
|
+
exports.requireRecordInput = requireRecordInput;
|
|
12891
15179
|
exports.requireRole = requireRole;
|
|
12892
15180
|
exports.requireRules = requireRules;
|
|
15181
|
+
exports.requireScope = requireScope;
|
|
12893
15182
|
exports.requireSectionsCap = requireSectionsCap;
|
|
12894
15183
|
exports.requireSummary = requireSummary;
|
|
15184
|
+
exports.requireWorkspace = requireWorkspace;
|
|
12895
15185
|
exports.resolveDecision = resolveDecision;
|
|
15186
|
+
exports.resolveDisclosure = resolveDisclosure;
|
|
12896
15187
|
exports.resolvePredict = resolvePredict;
|
|
15188
|
+
exports.resolveWorkspace = resolveWorkspace;
|
|
12897
15189
|
exports.sanitizeToken = sanitizeToken;
|
|
12898
15190
|
exports.sanitizeUsage = sanitizeUsage;
|
|
12899
15191
|
exports.scanAmendments = scanAmendments;
|
|
15192
|
+
exports.scanJobTree = scanJobTree;
|
|
12900
15193
|
exports.scanJudgmentTies = scanJudgmentTies;
|
|
12901
15194
|
exports.scanProbability = scanProbability;
|
|
12902
15195
|
exports.sectionContract = sectionContract;
|
|
@@ -12904,12 +15197,16 @@ exports.sectionShape = sectionShape;
|
|
|
12904
15197
|
exports.selectRecords = selectRecords;
|
|
12905
15198
|
exports.settleAgentJob = settleAgentJob;
|
|
12906
15199
|
exports.settleToolResult = settleToolResult;
|
|
15200
|
+
exports.skillInputContract = skillInputContract;
|
|
15201
|
+
exports.skillInputShape = skillInputShape;
|
|
12907
15202
|
exports.splitResultLines = splitResultLines;
|
|
12908
15203
|
exports.splitSentences = splitSentences;
|
|
12909
15204
|
exports.splitTopic = splitTopic;
|
|
12910
15205
|
exports.splitWords = splitWords;
|
|
12911
15206
|
exports.stripThinking = stripThinking;
|
|
12912
15207
|
exports.sumUsage = sumUsage;
|
|
15208
|
+
exports.switchToolContract = switchToolContract;
|
|
15209
|
+
exports.switchToolShape = switchToolShape;
|
|
12913
15210
|
exports.tokenUsageShape = tokenUsageShape;
|
|
12914
15211
|
exports.tokenUsageToJSON = tokenUsageToJSON;
|
|
12915
15212
|
exports.toolCallShape = toolCallShape;
|