@orkestrel/brief 0.0.4 → 0.0.5
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 +132 -8
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +63 -10
- package/dist/src/core/index.d.ts +63 -10
- package/dist/src/core/index.js +132 -9
- package/dist/src/core/index.js.map +1 -1
- package/package.json +3 -3
package/dist/src/core/index.cjs
CHANGED
|
@@ -370,6 +370,84 @@ var isBrief = (0, _orkestrel_contract.recordOf)({
|
|
|
370
370
|
//#endregion
|
|
371
371
|
//#region src/core/cloners.ts
|
|
372
372
|
/**
|
|
373
|
+
* Captures one stable, frozen view of a foreign contract value.
|
|
374
|
+
*
|
|
375
|
+
* @remarks
|
|
376
|
+
* Rebuilds the root and every reachable plain container from its own enumerable members.
|
|
377
|
+
* Unknown own members survive. Each published member absent from that copied own set is read
|
|
378
|
+
* once and materialized, which admits a class that supplies its contract through prototype
|
|
379
|
+
* accessors without leaving later reads attached to the live instance. Non-container leaves
|
|
380
|
+
* retain their identity, including functions that `structuredClone` cannot carry.
|
|
381
|
+
*
|
|
382
|
+
* @param source - The foreign value to capture.
|
|
383
|
+
* @param members - The published root member names to materialize when absent from its own set.
|
|
384
|
+
* @returns A deeply frozen plain view, or `source` itself when it is a primitive.
|
|
385
|
+
*
|
|
386
|
+
* @example
|
|
387
|
+
* ```ts
|
|
388
|
+
* import { captureValue } from '@orkestrel/brief'
|
|
389
|
+
*
|
|
390
|
+
* const leaf = () => 'ready'
|
|
391
|
+
* const owned = captureValue({ leaf }, ['leaf'])
|
|
392
|
+
* Reflect.get(owned, 'leaf') === leaf // true — an uncloneable leaf keeps its identity
|
|
393
|
+
* Object.isFrozen(owned) // true
|
|
394
|
+
* ```
|
|
395
|
+
*/
|
|
396
|
+
function captureValue(source, members) {
|
|
397
|
+
if (source === null || typeof source !== "object" && typeof source !== "function") return source;
|
|
398
|
+
const target = Array.isArray(source) ? [] : Object.create(null);
|
|
399
|
+
const seen = new WeakMap([[source, target]]);
|
|
400
|
+
const captured = [target];
|
|
401
|
+
const pending = [[
|
|
402
|
+
source,
|
|
403
|
+
target,
|
|
404
|
+
members
|
|
405
|
+
]];
|
|
406
|
+
while (pending.length > 0) {
|
|
407
|
+
const frame = pending.pop();
|
|
408
|
+
if (frame === void 0) continue;
|
|
409
|
+
const [current, view, expected] = frame;
|
|
410
|
+
const entries = [];
|
|
411
|
+
const copied = /* @__PURE__ */ new Set();
|
|
412
|
+
for (const key of Reflect.ownKeys(current)) {
|
|
413
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(current, key);
|
|
414
|
+
if (descriptor === void 0 || !descriptor.enumerable) continue;
|
|
415
|
+
copied.add(key);
|
|
416
|
+
entries.push([key, "value" in descriptor ? descriptor.value : Reflect.get(current, key)]);
|
|
417
|
+
}
|
|
418
|
+
for (const key of expected ?? []) if (!copied.has(key)) entries.push([key, Reflect.get(current, key)]);
|
|
419
|
+
for (const [key, value] of entries) {
|
|
420
|
+
let owned = value;
|
|
421
|
+
if (value !== null && typeof value === "object") {
|
|
422
|
+
const existing = seen.get(value);
|
|
423
|
+
if (existing !== void 0) owned = existing;
|
|
424
|
+
else {
|
|
425
|
+
const prototype = Reflect.getPrototypeOf(value);
|
|
426
|
+
if (Array.isArray(value) || prototype === null || prototype === Object.prototype) {
|
|
427
|
+
const branch = Array.isArray(value) ? [] : Object.create(null);
|
|
428
|
+
seen.set(value, branch);
|
|
429
|
+
captured.push(branch);
|
|
430
|
+
pending.push([
|
|
431
|
+
value,
|
|
432
|
+
branch,
|
|
433
|
+
void 0
|
|
434
|
+
]);
|
|
435
|
+
owned = branch;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
Reflect.defineProperty(view, key, {
|
|
440
|
+
value: owned,
|
|
441
|
+
enumerable: true,
|
|
442
|
+
configurable: false,
|
|
443
|
+
writable: false
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
for (const view of captured) Object.freeze(view);
|
|
448
|
+
return target;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
373
451
|
* Return a deeply owned, deeply frozen copy of a brief, refusing anything off-contract.
|
|
374
452
|
*
|
|
375
453
|
* @remarks
|
|
@@ -1164,6 +1242,15 @@ function errorToMessage(error) {
|
|
|
1164
1242
|
* nothing, so it is an assertion rather than a factory. Reserve it for programmer-error
|
|
1165
1243
|
* contexts where invalidity is a bug.
|
|
1166
1244
|
*
|
|
1245
|
+
* Intake NARROWS, and transfers no ownership. What comes back is the caller's own object,
|
|
1246
|
+
* so a member carried on an accessor can answer this guard one way and a later reader
|
|
1247
|
+
* another. The division is deliberate: the borrowed-engine law governs values this package
|
|
1248
|
+
* pulls across a seam it called, and a value handed in at the door stays the caller's.
|
|
1249
|
+
* `snapshotBrief` is the ownership door, and `pinBrief`, `BriefManager`, `briefToMarkdown`,
|
|
1250
|
+
* `briefToGoal`, and `briefToDispatch` take it. `briefToSubject`, `briefToContent`, and
|
|
1251
|
+
* `briefToTrace` read the value they are handed instead, so a caller reaching one of those
|
|
1252
|
+
* directly owns that reading. Pass `assertBrief` a value you already own.
|
|
1253
|
+
*
|
|
1167
1254
|
* @param data - The candidate brief data.
|
|
1168
1255
|
* @returns The same value, now known to satisfy {@link Brief}.
|
|
1169
1256
|
* @throws {@link BriefError} `INVALID` when `data` fails `isBrief`.
|
|
@@ -1503,8 +1590,10 @@ function deriveStatement(text) {
|
|
|
1503
1590
|
* ```
|
|
1504
1591
|
*/
|
|
1505
1592
|
function deriveTask(intent, text, actions, domains) {
|
|
1506
|
-
const
|
|
1507
|
-
const
|
|
1593
|
+
const operationDescriptor = Object.getOwnPropertyDescriptor(actions, intent.action);
|
|
1594
|
+
const domainDescriptor = Object.getOwnPropertyDescriptor(domains, intent.domain);
|
|
1595
|
+
const operation = operationDescriptor === void 0 ? void 0 : "value" in operationDescriptor ? operationDescriptor.value : operationDescriptor.get === void 0 ? void 0 : Reflect.apply(operationDescriptor.get, actions, []);
|
|
1596
|
+
const domain = domainDescriptor === void 0 ? void 0 : "value" in domainDescriptor ? domainDescriptor.value : domainDescriptor.get === void 0 ? void 0 : Reflect.apply(domainDescriptor.get, domains, []);
|
|
1508
1597
|
if (!isTaskOperation(operation) || !isTaskDomain(domain)) return void 0;
|
|
1509
1598
|
const statement = deriveStatement(text);
|
|
1510
1599
|
return statement.length === 0 ? void 0 : task(operation, domain, statement);
|
|
@@ -1570,6 +1659,15 @@ function deriveGaps(ambiguities) {
|
|
|
1570
1659
|
* all fail the same way — `undefined`, never a throw. Coerce a bare vocabulary value with
|
|
1571
1660
|
* `parseEnum` from `@orkestrel/contract` against the exported tuple instead.
|
|
1572
1661
|
*
|
|
1662
|
+
* The half of the intake pair that is OWNED BY CONSTRUCTION, which is what separates it from
|
|
1663
|
+
* `assertBrief`. The argument is text, so the graph the guard reads is one `JSON.parse` built
|
|
1664
|
+
* inside this call: it carries no caller identity, no accessor, and no alias back into anything
|
|
1665
|
+
* the caller still holds, and the parse-and-guard primitive this file imports from
|
|
1666
|
+
* `@orkestrel/contract` returns that same parsed graph rather than a second reading of it.
|
|
1667
|
+
* Every member `isBrief` checked therefore answers a later reader identically. The value is
|
|
1668
|
+
* fresh rather than frozen, so the caller owns it outright — reach for `snapshotBrief` when the
|
|
1669
|
+
* value came from code instead of from text.
|
|
1670
|
+
*
|
|
1573
1671
|
* @param value - The JSON text to parse.
|
|
1574
1672
|
* @returns The `Brief` when the parsed value satisfies `isBrief`, otherwise `undefined`.
|
|
1575
1673
|
*
|
|
@@ -1880,7 +1978,15 @@ var BriefCompiler = class {
|
|
|
1880
1978
|
}
|
|
1881
1979
|
gate(source) {
|
|
1882
1980
|
this.#refuseDestroyed();
|
|
1883
|
-
const ruled = (0, _orkestrel_contract.attempt)(() => this.#own(this.#reason.reason(briefToSubject(source), gateDefinition())
|
|
1981
|
+
const ruled = (0, _orkestrel_contract.attempt)(() => this.#own(this.#reason.reason(briefToSubject(source), gateDefinition()), [
|
|
1982
|
+
"reasoning",
|
|
1983
|
+
"conclusion",
|
|
1984
|
+
"rules",
|
|
1985
|
+
"count",
|
|
1986
|
+
"success",
|
|
1987
|
+
"trace",
|
|
1988
|
+
"errors"
|
|
1989
|
+
]));
|
|
1884
1990
|
if (!ruled.success) throw new BriefError("GATE_FAILED", errorToMessage(ruled.error), {
|
|
1885
1991
|
stage: "gate",
|
|
1886
1992
|
field: "reason"
|
|
@@ -1903,14 +2009,30 @@ var BriefCompiler = class {
|
|
|
1903
2009
|
#snapshot(input) {
|
|
1904
2010
|
return freezeDeep(structuredClone(input));
|
|
1905
2011
|
}
|
|
1906
|
-
#own(value) {
|
|
2012
|
+
#own(value, members) {
|
|
1907
2013
|
const cloned = (0, _orkestrel_contract.attempt)(() => structuredClone(value));
|
|
1908
|
-
return
|
|
2014
|
+
return cloned.success ? freezeDeep(cloned.value) : captureValue(value, members);
|
|
1909
2015
|
}
|
|
1910
2016
|
#read(input, raw, stages, failures) {
|
|
2017
|
+
const members = [
|
|
2018
|
+
"text",
|
|
2019
|
+
"normalized",
|
|
2020
|
+
"intent",
|
|
2021
|
+
"entities",
|
|
2022
|
+
"subject",
|
|
2023
|
+
"definition",
|
|
2024
|
+
"mappings",
|
|
2025
|
+
"ambiguities",
|
|
2026
|
+
"prompt",
|
|
2027
|
+
"stages",
|
|
2028
|
+
"failures",
|
|
2029
|
+
"complete",
|
|
2030
|
+
"confidence",
|
|
2031
|
+
"digest"
|
|
2032
|
+
];
|
|
1911
2033
|
const text = input.text;
|
|
1912
2034
|
if (text !== void 0) {
|
|
1913
|
-
const read = (0, _orkestrel_contract.attempt)(() => this.#own(this.#interpret.interpret(text)));
|
|
2035
|
+
const read = (0, _orkestrel_contract.attempt)(() => this.#own(this.#interpret.interpret(text), members));
|
|
1914
2036
|
if (read.success && (0, _orkestrel_interpret.isInterpretation)(read.value)) {
|
|
1915
2037
|
stages.push(Object.freeze({
|
|
1916
2038
|
stage: "interpret",
|
|
@@ -1935,7 +2057,8 @@ var BriefCompiler = class {
|
|
|
1935
2057
|
const supplied = input.interpretation;
|
|
1936
2058
|
if (supplied === void 0 || (0, _orkestrel_interpret.isInterpretation)(supplied)) return supplied;
|
|
1937
2059
|
const live = raw.interpretation;
|
|
1938
|
-
|
|
2060
|
+
const captured = (0, _orkestrel_contract.attempt)(() => captureValue(live, members));
|
|
2061
|
+
if (captured.success && (0, _orkestrel_interpret.isInterpretation)(captured.value)) return captured.value;
|
|
1939
2062
|
const message = "The supplied interpretation does not satisfy the published shape";
|
|
1940
2063
|
stages.push(Object.freeze({
|
|
1941
2064
|
stage: "interpret",
|
|
@@ -1959,7 +2082,7 @@ var BriefCompiler = class {
|
|
|
1959
2082
|
code: "BLOCKED",
|
|
1960
2083
|
message: `Gate refused: ${unready.join(", ")}`
|
|
1961
2084
|
};
|
|
1962
|
-
if (
|
|
2085
|
+
if (verdict === void 0) return void 0;
|
|
1963
2086
|
const refused = verdict.rules.filter((entry) => !entry.conclusion).map((entry) => entry.id).join(", ");
|
|
1964
2087
|
if (refused.length === 0) return {
|
|
1965
2088
|
stage: "gate",
|
|
@@ -2115,6 +2238,7 @@ exports.briefToHash = briefToHash;
|
|
|
2115
2238
|
exports.briefToMarkdown = briefToMarkdown;
|
|
2116
2239
|
exports.briefToSubject = briefToSubject;
|
|
2117
2240
|
exports.briefToTrace = briefToTrace;
|
|
2241
|
+
exports.captureValue = captureValue;
|
|
2118
2242
|
exports.citation = citation;
|
|
2119
2243
|
exports.citationShape = citationShape;
|
|
2120
2244
|
exports.countSentences = countSentences;
|