@orkestrel/brief 0.0.4 → 0.0.6
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 +147 -8
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +79 -10
- package/dist/src/core/index.d.ts +79 -10
- package/dist/src/core/index.js +146 -9
- package/dist/src/core/index.js.map +1 -1
- package/package.json +13 -12
package/dist/src/core/index.cjs
CHANGED
|
@@ -45,6 +45,36 @@ var RISK_SEVERITIES = Object.freeze([
|
|
|
45
45
|
"high"
|
|
46
46
|
]);
|
|
47
47
|
/**
|
|
48
|
+
* Every published `Interpretation` member name, frozen.
|
|
49
|
+
*
|
|
50
|
+
* @remarks
|
|
51
|
+
* The capture list `BriefCompiler` hands `captureValue` at each interpret door — the borrowed
|
|
52
|
+
* engine's return, and the caller's supplied interpretation. A class instance carries its
|
|
53
|
+
* contract on the prototype, so the captured view materializes exactly the members named here,
|
|
54
|
+
* and a name missing from the list is a member the view drops.
|
|
55
|
+
*
|
|
56
|
+
* The `satisfies` clause refuses a name `Interpretation` does not declare, and it holds the
|
|
57
|
+
* element type at the listed names rather than widening it to `string`. That is what lets the
|
|
58
|
+
* equality assertion beside the capture cases refuse a list that has fallen short of the
|
|
59
|
+
* published shape.
|
|
60
|
+
*/
|
|
61
|
+
var INTERPRETATION_MEMBERS = Object.freeze([
|
|
62
|
+
"text",
|
|
63
|
+
"normalized",
|
|
64
|
+
"intent",
|
|
65
|
+
"entities",
|
|
66
|
+
"subject",
|
|
67
|
+
"definition",
|
|
68
|
+
"mappings",
|
|
69
|
+
"ambiguities",
|
|
70
|
+
"prompt",
|
|
71
|
+
"stages",
|
|
72
|
+
"failures",
|
|
73
|
+
"complete",
|
|
74
|
+
"confidence",
|
|
75
|
+
"digest"
|
|
76
|
+
]);
|
|
77
|
+
/**
|
|
48
78
|
* `16` — the default turn cap `briefToGoal` renders.
|
|
49
79
|
*
|
|
50
80
|
* @remarks
|
|
@@ -370,6 +400,84 @@ var isBrief = (0, _orkestrel_contract.recordOf)({
|
|
|
370
400
|
//#endregion
|
|
371
401
|
//#region src/core/cloners.ts
|
|
372
402
|
/**
|
|
403
|
+
* Captures one stable, frozen view of a foreign contract value.
|
|
404
|
+
*
|
|
405
|
+
* @remarks
|
|
406
|
+
* Rebuilds the root and every reachable plain container from its own enumerable members.
|
|
407
|
+
* Unknown own members survive. Each published member absent from that copied own set is read
|
|
408
|
+
* once and materialized, which admits a class that supplies its contract through prototype
|
|
409
|
+
* accessors without leaving later reads attached to the live instance. Non-container leaves
|
|
410
|
+
* retain their identity, including functions that `structuredClone` cannot carry.
|
|
411
|
+
*
|
|
412
|
+
* @param source - The foreign value to capture.
|
|
413
|
+
* @param members - The published root member names to materialize when absent from its own set.
|
|
414
|
+
* @returns A deeply frozen plain view, or `source` itself when it is a primitive.
|
|
415
|
+
*
|
|
416
|
+
* @example
|
|
417
|
+
* ```ts
|
|
418
|
+
* import { captureValue } from '@orkestrel/brief'
|
|
419
|
+
*
|
|
420
|
+
* const leaf = () => 'ready'
|
|
421
|
+
* const owned = captureValue({ leaf }, ['leaf'])
|
|
422
|
+
* Reflect.get(owned, 'leaf') === leaf // true — an uncloneable leaf keeps its identity
|
|
423
|
+
* Object.isFrozen(owned) // true
|
|
424
|
+
* ```
|
|
425
|
+
*/
|
|
426
|
+
function captureValue(source, members) {
|
|
427
|
+
if (source === null || typeof source !== "object" && typeof source !== "function") return source;
|
|
428
|
+
const target = Array.isArray(source) ? [] : Object.create(null);
|
|
429
|
+
const seen = new WeakMap([[source, target]]);
|
|
430
|
+
const captured = [target];
|
|
431
|
+
const pending = [[
|
|
432
|
+
source,
|
|
433
|
+
target,
|
|
434
|
+
members
|
|
435
|
+
]];
|
|
436
|
+
while (pending.length > 0) {
|
|
437
|
+
const frame = pending.pop();
|
|
438
|
+
if (frame === void 0) continue;
|
|
439
|
+
const [current, view, expected] = frame;
|
|
440
|
+
const entries = [];
|
|
441
|
+
const copied = /* @__PURE__ */ new Set();
|
|
442
|
+
for (const key of Reflect.ownKeys(current)) {
|
|
443
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(current, key);
|
|
444
|
+
if (descriptor === void 0 || !descriptor.enumerable) continue;
|
|
445
|
+
copied.add(key);
|
|
446
|
+
entries.push([key, "value" in descriptor ? descriptor.value : Reflect.get(current, key)]);
|
|
447
|
+
}
|
|
448
|
+
for (const key of expected ?? []) if (!copied.has(key)) entries.push([key, Reflect.get(current, key)]);
|
|
449
|
+
for (const [key, value] of entries) {
|
|
450
|
+
let owned = value;
|
|
451
|
+
if (value !== null && typeof value === "object") {
|
|
452
|
+
const existing = seen.get(value);
|
|
453
|
+
if (existing !== void 0) owned = existing;
|
|
454
|
+
else {
|
|
455
|
+
const prototype = Reflect.getPrototypeOf(value);
|
|
456
|
+
if (Array.isArray(value) || prototype === null || prototype === Object.prototype) {
|
|
457
|
+
const branch = Array.isArray(value) ? [] : Object.create(null);
|
|
458
|
+
seen.set(value, branch);
|
|
459
|
+
captured.push(branch);
|
|
460
|
+
pending.push([
|
|
461
|
+
value,
|
|
462
|
+
branch,
|
|
463
|
+
void 0
|
|
464
|
+
]);
|
|
465
|
+
owned = branch;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
Reflect.defineProperty(view, key, {
|
|
470
|
+
value: owned,
|
|
471
|
+
enumerable: true,
|
|
472
|
+
configurable: false,
|
|
473
|
+
writable: false
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
for (const view of captured) Object.freeze(view);
|
|
478
|
+
return target;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
373
481
|
* Return a deeply owned, deeply frozen copy of a brief, refusing anything off-contract.
|
|
374
482
|
*
|
|
375
483
|
* @remarks
|
|
@@ -1164,6 +1272,15 @@ function errorToMessage(error) {
|
|
|
1164
1272
|
* nothing, so it is an assertion rather than a factory. Reserve it for programmer-error
|
|
1165
1273
|
* contexts where invalidity is a bug.
|
|
1166
1274
|
*
|
|
1275
|
+
* Intake NARROWS, and transfers no ownership. What comes back is the caller's own object,
|
|
1276
|
+
* so a member carried on an accessor can answer this guard one way and a later reader
|
|
1277
|
+
* another. The division is deliberate: the borrowed-engine law governs values this package
|
|
1278
|
+
* pulls across a seam it called, and a value handed in at the door stays the caller's.
|
|
1279
|
+
* `snapshotBrief` is the ownership door, and `pinBrief`, `BriefManager`, `briefToMarkdown`,
|
|
1280
|
+
* `briefToGoal`, and `briefToDispatch` take it. `briefToSubject`, `briefToContent`, and
|
|
1281
|
+
* `briefToTrace` read the value they are handed instead, so a caller reaching one of those
|
|
1282
|
+
* directly owns that reading. Pass `assertBrief` a value you already own.
|
|
1283
|
+
*
|
|
1167
1284
|
* @param data - The candidate brief data.
|
|
1168
1285
|
* @returns The same value, now known to satisfy {@link Brief}.
|
|
1169
1286
|
* @throws {@link BriefError} `INVALID` when `data` fails `isBrief`.
|
|
@@ -1503,8 +1620,10 @@ function deriveStatement(text) {
|
|
|
1503
1620
|
* ```
|
|
1504
1621
|
*/
|
|
1505
1622
|
function deriveTask(intent, text, actions, domains) {
|
|
1506
|
-
const
|
|
1507
|
-
const
|
|
1623
|
+
const operationDescriptor = Object.getOwnPropertyDescriptor(actions, intent.action);
|
|
1624
|
+
const domainDescriptor = Object.getOwnPropertyDescriptor(domains, intent.domain);
|
|
1625
|
+
const operation = operationDescriptor === void 0 ? void 0 : "value" in operationDescriptor ? operationDescriptor.value : operationDescriptor.get === void 0 ? void 0 : Reflect.apply(operationDescriptor.get, actions, []);
|
|
1626
|
+
const domain = domainDescriptor === void 0 ? void 0 : "value" in domainDescriptor ? domainDescriptor.value : domainDescriptor.get === void 0 ? void 0 : Reflect.apply(domainDescriptor.get, domains, []);
|
|
1508
1627
|
if (!isTaskOperation(operation) || !isTaskDomain(domain)) return void 0;
|
|
1509
1628
|
const statement = deriveStatement(text);
|
|
1510
1629
|
return statement.length === 0 ? void 0 : task(operation, domain, statement);
|
|
@@ -1570,6 +1689,15 @@ function deriveGaps(ambiguities) {
|
|
|
1570
1689
|
* all fail the same way — `undefined`, never a throw. Coerce a bare vocabulary value with
|
|
1571
1690
|
* `parseEnum` from `@orkestrel/contract` against the exported tuple instead.
|
|
1572
1691
|
*
|
|
1692
|
+
* The half of the intake pair that is OWNED BY CONSTRUCTION, which is what separates it from
|
|
1693
|
+
* `assertBrief`. The argument is text, so the graph the guard reads is one `JSON.parse` built
|
|
1694
|
+
* inside this call: it carries no caller identity, no accessor, and no alias back into anything
|
|
1695
|
+
* the caller still holds, and the parse-and-guard primitive this file imports from
|
|
1696
|
+
* `@orkestrel/contract` returns that same parsed graph rather than a second reading of it.
|
|
1697
|
+
* Every member `isBrief` checked therefore answers a later reader identically. The value is
|
|
1698
|
+
* fresh rather than frozen, so the caller owns it outright — reach for `snapshotBrief` when the
|
|
1699
|
+
* value came from code instead of from text.
|
|
1700
|
+
*
|
|
1573
1701
|
* @param value - The JSON text to parse.
|
|
1574
1702
|
* @returns The `Brief` when the parsed value satisfies `isBrief`, otherwise `undefined`.
|
|
1575
1703
|
*
|
|
@@ -1880,7 +2008,15 @@ var BriefCompiler = class {
|
|
|
1880
2008
|
}
|
|
1881
2009
|
gate(source) {
|
|
1882
2010
|
this.#refuseDestroyed();
|
|
1883
|
-
const ruled = (0, _orkestrel_contract.attempt)(() => this.#own(this.#reason.reason(briefToSubject(source), gateDefinition())
|
|
2011
|
+
const ruled = (0, _orkestrel_contract.attempt)(() => this.#own(this.#reason.reason(briefToSubject(source), gateDefinition()), [
|
|
2012
|
+
"reasoning",
|
|
2013
|
+
"conclusion",
|
|
2014
|
+
"rules",
|
|
2015
|
+
"count",
|
|
2016
|
+
"success",
|
|
2017
|
+
"trace",
|
|
2018
|
+
"errors"
|
|
2019
|
+
]));
|
|
1884
2020
|
if (!ruled.success) throw new BriefError("GATE_FAILED", errorToMessage(ruled.error), {
|
|
1885
2021
|
stage: "gate",
|
|
1886
2022
|
field: "reason"
|
|
@@ -1903,14 +2039,14 @@ var BriefCompiler = class {
|
|
|
1903
2039
|
#snapshot(input) {
|
|
1904
2040
|
return freezeDeep(structuredClone(input));
|
|
1905
2041
|
}
|
|
1906
|
-
#own(value) {
|
|
2042
|
+
#own(value, members) {
|
|
1907
2043
|
const cloned = (0, _orkestrel_contract.attempt)(() => structuredClone(value));
|
|
1908
|
-
return
|
|
2044
|
+
return cloned.success ? freezeDeep(cloned.value) : captureValue(value, members);
|
|
1909
2045
|
}
|
|
1910
2046
|
#read(input, raw, stages, failures) {
|
|
1911
2047
|
const text = input.text;
|
|
1912
2048
|
if (text !== void 0) {
|
|
1913
|
-
const read = (0, _orkestrel_contract.attempt)(() => this.#own(this.#interpret.interpret(text)));
|
|
2049
|
+
const read = (0, _orkestrel_contract.attempt)(() => this.#own(this.#interpret.interpret(text), INTERPRETATION_MEMBERS));
|
|
1914
2050
|
if (read.success && (0, _orkestrel_interpret.isInterpretation)(read.value)) {
|
|
1915
2051
|
stages.push(Object.freeze({
|
|
1916
2052
|
stage: "interpret",
|
|
@@ -1935,7 +2071,8 @@ var BriefCompiler = class {
|
|
|
1935
2071
|
const supplied = input.interpretation;
|
|
1936
2072
|
if (supplied === void 0 || (0, _orkestrel_interpret.isInterpretation)(supplied)) return supplied;
|
|
1937
2073
|
const live = raw.interpretation;
|
|
1938
|
-
|
|
2074
|
+
const captured = (0, _orkestrel_contract.attempt)(() => captureValue(live, INTERPRETATION_MEMBERS));
|
|
2075
|
+
if (captured.success && (0, _orkestrel_interpret.isInterpretation)(captured.value)) return captured.value;
|
|
1939
2076
|
const message = "The supplied interpretation does not satisfy the published shape";
|
|
1940
2077
|
stages.push(Object.freeze({
|
|
1941
2078
|
stage: "interpret",
|
|
@@ -1959,7 +2096,7 @@ var BriefCompiler = class {
|
|
|
1959
2096
|
code: "BLOCKED",
|
|
1960
2097
|
message: `Gate refused: ${unready.join(", ")}`
|
|
1961
2098
|
};
|
|
1962
|
-
if (
|
|
2099
|
+
if (verdict === void 0) return void 0;
|
|
1963
2100
|
const refused = verdict.rules.filter((entry) => !entry.conclusion).map((entry) => entry.id).join(", ");
|
|
1964
2101
|
if (refused.length === 0) return {
|
|
1965
2102
|
stage: "gate",
|
|
@@ -2099,6 +2236,7 @@ exports.BriefError = BriefError;
|
|
|
2099
2236
|
exports.BriefManager = BriefManager;
|
|
2100
2237
|
exports.DEFAULT_BRIEF_TURNS = DEFAULT_BRIEF_TURNS;
|
|
2101
2238
|
exports.GATE_ID = GATE_ID;
|
|
2239
|
+
exports.INTERPRETATION_MEMBERS = INTERPRETATION_MEMBERS;
|
|
2102
2240
|
exports.LINE_BREAK_PATTERN = LINE_BREAK_PATTERN;
|
|
2103
2241
|
exports.OUTPUT_FORMATS = OUTPUT_FORMATS;
|
|
2104
2242
|
exports.RISK_SEVERITIES = RISK_SEVERITIES;
|
|
@@ -2115,6 +2253,7 @@ exports.briefToHash = briefToHash;
|
|
|
2115
2253
|
exports.briefToMarkdown = briefToMarkdown;
|
|
2116
2254
|
exports.briefToSubject = briefToSubject;
|
|
2117
2255
|
exports.briefToTrace = briefToTrace;
|
|
2256
|
+
exports.captureValue = captureValue;
|
|
2118
2257
|
exports.citation = citation;
|
|
2119
2258
|
exports.citationShape = citationShape;
|
|
2120
2259
|
exports.countSentences = countSentences;
|