@intutic/shared-types 2.9.1 → 3.0.1
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/decide.d.ts +269 -0
- package/dist/decide.d.ts.map +1 -0
- package/dist/decide.js +781 -0
- package/dist/decide.js.map +1 -0
- package/dist/decideEngine.d.ts +195 -0
- package/dist/decideEngine.d.ts.map +1 -0
- package/dist/decideEngine.js +720 -0
- package/dist/decideEngine.js.map +1 -0
- package/dist/decideEvaluate.d.ts +250 -0
- package/dist/decideEvaluate.d.ts.map +1 -0
- package/dist/decideEvaluate.js +518 -0
- package/dist/decideEvaluate.js.map +1 -0
- package/dist/decideHostGuard.d.ts +56 -0
- package/dist/decideHostGuard.d.ts.map +1 -0
- package/dist/decideHostGuard.js +200 -0
- package/dist/decideHostGuard.js.map +1 -0
- package/dist/decideLog.d.ts +287 -0
- package/dist/decideLog.d.ts.map +1 -0
- package/dist/decideLog.js +543 -0
- package/dist/decideLog.js.map +1 -0
- package/dist/decideLogRedaction.d.ts +12 -0
- package/dist/decideLogRedaction.d.ts.map +1 -0
- package/dist/decideLogRedaction.js +19 -0
- package/dist/decideLogRedaction.js.map +1 -0
- package/dist/decideRecord.d.ts +128 -0
- package/dist/decideRecord.d.ts.map +1 -0
- package/dist/decideRecord.js +266 -0
- package/dist/decideRecord.js.map +1 -0
- package/dist/decideSdk.d.ts +86 -0
- package/dist/decideSdk.d.ts.map +1 -0
- package/dist/decideSdk.js +151 -0
- package/dist/decideSdk.js.map +1 -0
- package/dist/errors.d.ts +11 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +12 -1
- package/dist/errors.js.map +1 -1
- package/dist/gateLimits.d.ts +27 -0
- package/dist/gateLimits.d.ts.map +1 -1
- package/dist/gateLimits.js +29 -1
- package/dist/gateLimits.js.map +1 -1
- package/dist/gateRuleRecord.d.ts +244 -0
- package/dist/gateRuleRecord.d.ts.map +1 -0
- package/dist/gateRuleRecord.js +474 -0
- package/dist/gateRuleRecord.js.map +1 -0
- package/dist/guardrailIr.d.ts +301 -10
- package/dist/guardrailIr.d.ts.map +1 -1
- package/dist/guardrailIr.js +86 -7
- package/dist/guardrailIr.js.map +1 -1
- package/dist/guardrailRender.d.ts +35 -2
- package/dist/guardrailRender.d.ts.map +1 -1
- package/dist/guardrailRender.js +133 -7
- package/dist/guardrailRender.js.map +1 -1
- package/dist/index.d.ts +28 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +162 -8
- package/dist/index.js.map +1 -1
- package/dist/lanes.d.ts +94 -0
- package/dist/lanes.d.ts.map +1 -0
- package/dist/lanes.js +160 -0
- package/dist/lanes.js.map +1 -0
- package/dist/loopVerify.d.ts +49 -0
- package/dist/loopVerify.d.ts.map +1 -0
- package/dist/loopVerify.js +77 -0
- package/dist/loopVerify.js.map +1 -0
- package/dist/mcpBudgets.d.ts +8 -8
- package/dist/notifications.d.ts +1 -1
- package/dist/notifications.d.ts.map +1 -1
- package/dist/notifications.js +14 -0
- package/dist/notifications.js.map +1 -1
- package/dist/providers.d.ts +13 -0
- package/dist/providers.d.ts.map +1 -1
- package/dist/providers.js +31 -0
- package/dist/providers.js.map +1 -1
- package/dist/refusalDetail.d.ts +58 -0
- package/dist/refusalDetail.d.ts.map +1 -0
- package/dist/refusalDetail.js +128 -0
- package/dist/refusalDetail.js.map +1 -0
- package/dist/responses/decide.d.ts +1858 -0
- package/dist/responses/decide.d.ts.map +1 -0
- package/dist/responses/decide.js +166 -0
- package/dist/responses/decide.js.map +1 -0
- package/dist/responses/gateways.d.ts +745 -101
- package/dist/responses/gateways.d.ts.map +1 -1
- package/dist/responses/gateways.js +34 -2
- package/dist/responses/gateways.js.map +1 -1
- package/dist/responses/guardrails.d.ts +1434 -144
- package/dist/responses/guardrails.d.ts.map +1 -1
- package/dist/responses/identity.d.ts +6 -6
- package/dist/responses/index.d.ts +1 -0
- package/dist/responses/index.d.ts.map +1 -1
- package/dist/responses/index.js +1 -0
- package/dist/responses/index.js.map +1 -1
- package/dist/responses/integrations.d.ts +84 -84
- package/dist/responses/observability.d.ts +272 -92
- package/dist/responses/observability.d.ts.map +1 -1
- package/dist/responses/observability.js +31 -1
- package/dist/responses/observability.js.map +1 -1
- package/dist/responses/rules.d.ts +1026 -187
- package/dist/responses/rules.d.ts.map +1 -1
- package/dist/responses/rules.js +47 -1
- package/dist/responses/rules.js.map +1 -1
- package/dist/scopeMatch.d.ts +156 -0
- package/dist/scopeMatch.d.ts.map +1 -0
- package/dist/scopeMatch.js +451 -0
- package/dist/scopeMatch.js.map +1 -0
- package/dist/sopKeySince.d.ts +41 -0
- package/dist/sopKeySince.d.ts.map +1 -0
- package/dist/sopKeySince.js +81 -0
- package/dist/sopKeySince.js.map +1 -0
- package/dist/testOutcome.d.ts +164 -0
- package/dist/testOutcome.d.ts.map +1 -0
- package/dist/testOutcome.js +454 -0
- package/dist/testOutcome.js.map +1 -0
- package/dist/upstreamRetry.d.ts +2 -2
- package/dist/workspaceSettings.d.ts +91 -0
- package/dist/workspaceSettings.d.ts.map +1 -1
- package/dist/workspaceSettings.js +48 -2
- package/dist/workspaceSettings.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decide rules in the policy snapshot: the `@format` record, the `@decide`
|
|
3
|
+
* record, and the id and reason of a rule's fallback line.
|
|
4
|
+
*
|
|
5
|
+
* A gate that cannot evaluate a decide rule must refuse what the rule covers,
|
|
6
|
+
* never skip it. So the snapshot writer emits, for each rule a gate is to
|
|
7
|
+
* enforce, two lines inside the digest:
|
|
8
|
+
*
|
|
9
|
+
* - an `@decide` record of five tab-separated columns (the tag, the fallback
|
|
10
|
+
* line's id or `-`, the stamped rollout, the comma-joined tools, and base64
|
|
11
|
+
* of the record's canonical JSON). Every 2.x rule parser needs six columns,
|
|
12
|
+
* so it skips the record;
|
|
13
|
+
* - a fallback line, an ordinary `block` rule on the rule's tools whose id
|
|
14
|
+
* starts `decide-fallback:`, which every gate since 2.0 enforces.
|
|
15
|
+
*
|
|
16
|
+
* A gate that reads decide rules skips a fallback line only when a record
|
|
17
|
+
* with that `fallbackId` decodes ({@link decodeDecideRecord}): a damaged
|
|
18
|
+
* record leaves its fallback line in force, which refuses. The `@format`
|
|
19
|
+
* record names the snapshot format, 2 once any decide line is written; a
|
|
20
|
+
* snapshot without one is byte-identical to a format 1 snapshot.
|
|
21
|
+
*
|
|
22
|
+
* Self-contained (no imports, no module state) so `@intutic/gate` can carry
|
|
23
|
+
* a byte-identical copy (`packages/gate-js/src/decideRecord.ts`) and the
|
|
24
|
+
* JavaScript hook gates can embed the decoder ({@link DECIDE_RECORD_JS_SOURCE}).
|
|
25
|
+
* The rule itself is checked by the caller's {@link DecideRuleCheck}, which
|
|
26
|
+
* runs `validateDecideRule` and `decideRuleHash` from `./decide.ts`. The
|
|
27
|
+
* Python gate's port is `intutic_clawde/gate/decide_record.py`; both run the
|
|
28
|
+
* `record` group of `fixtures/decide-vectors.json`.
|
|
29
|
+
*
|
|
30
|
+
* @module
|
|
31
|
+
*/
|
|
32
|
+
import type { DecideRule } from './decide.js';
|
|
33
|
+
/** First column of the snapshot format record: `@format<TAB><integer>`. */
|
|
34
|
+
export declare const SNAPSHOT_FORMAT_RECORD_TAG = "@format";
|
|
35
|
+
/**
|
|
36
|
+
* The newest snapshot format a gate of this generation reads: 3, which adds
|
|
37
|
+
* the `@rule` records of the loops, graphs and gates release (`./gateRuleRecord.ts`). A gate reading a
|
|
38
|
+
* newer one reports `snapshot_format_newer` and enforces the fallback lines
|
|
39
|
+
* of the records it does not know.
|
|
40
|
+
*/
|
|
41
|
+
export declare const SNAPSHOT_FORMAT = 3;
|
|
42
|
+
/**
|
|
43
|
+
* The format a snapshot carrying decide lines and no `@rule` record is
|
|
44
|
+
* written in, so such a snapshot stays byte-identical to one written before rule records existed. One
|
|
45
|
+
* with any `@rule` record is format 3 (`RULE_SNAPSHOT_FORMAT`).
|
|
46
|
+
*/
|
|
47
|
+
export declare const DECIDE_SNAPSHOT_FORMAT = 2;
|
|
48
|
+
/** First column of a decide rule's record. */
|
|
49
|
+
export declare const DECIDE_RECORD_TAG = "@decide";
|
|
50
|
+
/** The id prefix of a decide rule's fallback line. */
|
|
51
|
+
export declare const DECIDE_FALLBACK_PREFIX = "decide-fallback:";
|
|
52
|
+
/** A record carries the first this many bytes of its SOP's body. */
|
|
53
|
+
export declare const DECIDE_RULE_TEXT_MAX_BYTES = 4096;
|
|
54
|
+
/** The longest SOP title a record carries, in code points. */
|
|
55
|
+
export declare const DECIDE_SOP_TITLE_MAX_CHARS = 80;
|
|
56
|
+
/** Why a rule's stamped rollout is what it is. Only `rule` can stamp `enforce`. */
|
|
57
|
+
export type DecideStamp = 'rule' | 'local-disable' | 'server-shadow' | 'silent-log' | 'no-pins';
|
|
58
|
+
/** A calibration a rule may enforce under: the model digest it was evaluated on, and what the evaluation fitted. */
|
|
59
|
+
export interface DecidePin {
|
|
60
|
+
modelDigest: string;
|
|
61
|
+
temperature?: Record<string, number>;
|
|
62
|
+
band?: {
|
|
63
|
+
yes_at: number;
|
|
64
|
+
no_below: number;
|
|
65
|
+
};
|
|
66
|
+
combiner?: {
|
|
67
|
+
bias: number;
|
|
68
|
+
weights: Record<string, number>;
|
|
69
|
+
};
|
|
70
|
+
evalRunId: string;
|
|
71
|
+
source: 'local' | 'server';
|
|
72
|
+
}
|
|
73
|
+
/** One `@decide` record's payload. */
|
|
74
|
+
export interface DecideRecord {
|
|
75
|
+
v: 1;
|
|
76
|
+
/** `<sopId>#<rule id>`. */
|
|
77
|
+
ref: string;
|
|
78
|
+
ruleHash: string;
|
|
79
|
+
/** The paired fallback line's id; null when the rule is stamped shadow and has none. */
|
|
80
|
+
fallbackId: string | null;
|
|
81
|
+
rollout: 'shadow' | 'enforce';
|
|
82
|
+
stampedBy: DecideStamp;
|
|
83
|
+
/** The rule as its `decide:` line carries it. */
|
|
84
|
+
ir: DecideRule;
|
|
85
|
+
sopTitle: string;
|
|
86
|
+
ruleText: string;
|
|
87
|
+
pins: DecidePin[];
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Checks a record's rule: the rule's `ruleHash` when it is a valid decide
|
|
91
|
+
* rule, null when it is not. Callers pass `validateDecideRule` then
|
|
92
|
+
* `decideRuleHash`, with their own SHA-256.
|
|
93
|
+
*/
|
|
94
|
+
export type DecideRuleCheck = (ir: unknown) => string | null;
|
|
95
|
+
/** `@format<TAB><format>`: 2 ({@link DECIDE_SNAPSHOT_FORMAT}) unless the writer passes 3 for a snapshot with `@rule` records. */
|
|
96
|
+
export declare function encodeSnapshotFormatRecord(format?: number): string;
|
|
97
|
+
/** The format a `@format` line names: a positive integer, or null for any other line or a damaged one. */
|
|
98
|
+
export declare function decodeSnapshotFormatRecord(line: string): number | null;
|
|
99
|
+
/** `ruleRef`: `<sopId>#<rule id>`. */
|
|
100
|
+
export declare function decideRuleRef(sopId: string, ruleId: string): string;
|
|
101
|
+
/** `fallbackId`: `decide-fallback:<rule id>@<first 12 hex of sha256(sopId)>`. `sha256Hex` hashes a string's UTF-8 bytes. */
|
|
102
|
+
export declare function decideFallbackId(ruleId: string, sopId: string, sha256Hex: (text: string) => string): string;
|
|
103
|
+
/** What an older gate says when a fallback line refuses. The writer scrubs and bounds it like any reason. */
|
|
104
|
+
export declare function decideFallbackReason(ruleId: string, sopTitle: string): string;
|
|
105
|
+
/** A SOP body as a record carries it: its first {@link DECIDE_RULE_TEXT_MAX_BYTES} bytes of UTF-8, never splitting a character. */
|
|
106
|
+
export declare function decideRuleText(body: string): string;
|
|
107
|
+
/** A pin as a record carries it, or null. Unknown fields refuse: a pin a reader cannot apply must not be read as a weaker one. */
|
|
108
|
+
export declare function parseDecidePin(value: unknown): DecidePin | null;
|
|
109
|
+
/** The record as one `.rules` line. */
|
|
110
|
+
export declare function encodeDecideRecord(record: DecideRecord): string;
|
|
111
|
+
/**
|
|
112
|
+
* Reads an `@decide` line back. Null, and so the fallback line stays in
|
|
113
|
+
* force, for any other line or when: it does not have five columns; the
|
|
114
|
+
* base64 or JSON is damaged; the payload has a field this reader does not
|
|
115
|
+
* know or lacks one; `check` finds the rule invalid or its hash differs from
|
|
116
|
+
* `ruleHash`; the stamp is inconsistent (only a rule stamped `enforce` by
|
|
117
|
+
* `rule` has a fallback line, and it is named for the rule); or columns 2 to
|
|
118
|
+
* 4 disagree with the payload.
|
|
119
|
+
*/
|
|
120
|
+
export declare function decodeDecideRecord(line: string, check: DecideRuleCheck): DecideRecord | null;
|
|
121
|
+
/**
|
|
122
|
+
* The decoders above as JavaScript source, for the hook gates to embed. The
|
|
123
|
+
* gate defines `check` with the embedded `./decide.ts` source and its own
|
|
124
|
+
* SHA-256. A CommonJS build names an exported constant `exports.X` inside a
|
|
125
|
+
* function; the source defines each as a plain `X`, so that prefix is dropped.
|
|
126
|
+
*/
|
|
127
|
+
export declare const DECIDE_RECORD_JS_SOURCE: string;
|
|
128
|
+
//# sourceMappingURL=decideRecord.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decideRecord.d.ts","sourceRoot":"","sources":["../src/decideRecord.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Decide rules in the policy snapshot: the `@format` record, the `@decide`
|
|
4
|
+
* record, and the id and reason of a rule's fallback line.
|
|
5
|
+
*
|
|
6
|
+
* A gate that cannot evaluate a decide rule must refuse what the rule covers,
|
|
7
|
+
* never skip it. So the snapshot writer emits, for each rule a gate is to
|
|
8
|
+
* enforce, two lines inside the digest:
|
|
9
|
+
*
|
|
10
|
+
* - an `@decide` record of five tab-separated columns (the tag, the fallback
|
|
11
|
+
* line's id or `-`, the stamped rollout, the comma-joined tools, and base64
|
|
12
|
+
* of the record's canonical JSON). Every 2.x rule parser needs six columns,
|
|
13
|
+
* so it skips the record;
|
|
14
|
+
* - a fallback line, an ordinary `block` rule on the rule's tools whose id
|
|
15
|
+
* starts `decide-fallback:`, which every gate since 2.0 enforces.
|
|
16
|
+
*
|
|
17
|
+
* A gate that reads decide rules skips a fallback line only when a record
|
|
18
|
+
* with that `fallbackId` decodes ({@link decodeDecideRecord}): a damaged
|
|
19
|
+
* record leaves its fallback line in force, which refuses. The `@format`
|
|
20
|
+
* record names the snapshot format, 2 once any decide line is written; a
|
|
21
|
+
* snapshot without one is byte-identical to a format 1 snapshot.
|
|
22
|
+
*
|
|
23
|
+
* Self-contained (no imports, no module state) so `@intutic/gate` can carry
|
|
24
|
+
* a byte-identical copy (`packages/gate-js/src/decideRecord.ts`) and the
|
|
25
|
+
* JavaScript hook gates can embed the decoder ({@link DECIDE_RECORD_JS_SOURCE}).
|
|
26
|
+
* The rule itself is checked by the caller's {@link DecideRuleCheck}, which
|
|
27
|
+
* runs `validateDecideRule` and `decideRuleHash` from `./decide.ts`. The
|
|
28
|
+
* Python gate's port is `intutic_clawde/gate/decide_record.py`; both run the
|
|
29
|
+
* `record` group of `fixtures/decide-vectors.json`.
|
|
30
|
+
*
|
|
31
|
+
* @module
|
|
32
|
+
*/
|
|
33
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
+
exports.DECIDE_RECORD_JS_SOURCE = exports.DECIDE_SOP_TITLE_MAX_CHARS = exports.DECIDE_RULE_TEXT_MAX_BYTES = exports.DECIDE_FALLBACK_PREFIX = exports.DECIDE_RECORD_TAG = exports.DECIDE_SNAPSHOT_FORMAT = exports.SNAPSHOT_FORMAT = exports.SNAPSHOT_FORMAT_RECORD_TAG = void 0;
|
|
35
|
+
exports.encodeSnapshotFormatRecord = encodeSnapshotFormatRecord;
|
|
36
|
+
exports.decodeSnapshotFormatRecord = decodeSnapshotFormatRecord;
|
|
37
|
+
exports.decideRuleRef = decideRuleRef;
|
|
38
|
+
exports.decideFallbackId = decideFallbackId;
|
|
39
|
+
exports.decideFallbackReason = decideFallbackReason;
|
|
40
|
+
exports.decideRuleText = decideRuleText;
|
|
41
|
+
exports.parseDecidePin = parseDecidePin;
|
|
42
|
+
exports.encodeDecideRecord = encodeDecideRecord;
|
|
43
|
+
exports.decodeDecideRecord = decodeDecideRecord;
|
|
44
|
+
/** First column of the snapshot format record: `@format<TAB><integer>`. */
|
|
45
|
+
exports.SNAPSHOT_FORMAT_RECORD_TAG = '@format';
|
|
46
|
+
/**
|
|
47
|
+
* The newest snapshot format a gate of this generation reads: 3, which adds
|
|
48
|
+
* the `@rule` records of the loops, graphs and gates release (`./gateRuleRecord.ts`). A gate reading a
|
|
49
|
+
* newer one reports `snapshot_format_newer` and enforces the fallback lines
|
|
50
|
+
* of the records it does not know.
|
|
51
|
+
*/
|
|
52
|
+
exports.SNAPSHOT_FORMAT = 3;
|
|
53
|
+
/**
|
|
54
|
+
* The format a snapshot carrying decide lines and no `@rule` record is
|
|
55
|
+
* written in, so such a snapshot stays byte-identical to one written before rule records existed. One
|
|
56
|
+
* with any `@rule` record is format 3 (`RULE_SNAPSHOT_FORMAT`).
|
|
57
|
+
*/
|
|
58
|
+
exports.DECIDE_SNAPSHOT_FORMAT = 2;
|
|
59
|
+
/** First column of a decide rule's record. */
|
|
60
|
+
exports.DECIDE_RECORD_TAG = '@decide';
|
|
61
|
+
/** The id prefix of a decide rule's fallback line. */
|
|
62
|
+
exports.DECIDE_FALLBACK_PREFIX = 'decide-fallback:';
|
|
63
|
+
/** A record carries the first this many bytes of its SOP's body. */
|
|
64
|
+
exports.DECIDE_RULE_TEXT_MAX_BYTES = 4096;
|
|
65
|
+
/** The longest SOP title a record carries, in code points. */
|
|
66
|
+
exports.DECIDE_SOP_TITLE_MAX_CHARS = 80;
|
|
67
|
+
const DECIDE_STAMPS = ['rule', 'local-disable', 'server-shadow', 'silent-log', 'no-pins'];
|
|
68
|
+
const DECIDE_RECORD_FIELDS = ['fallbackId', 'ir', 'pins', 'ref', 'rollout', 'ruleHash', 'ruleText', 'sopTitle', 'stampedBy', 'v'];
|
|
69
|
+
const DECIDE_PIN_FIELDS = ['band', 'combiner', 'evalRunId', 'modelDigest', 'source', 'temperature'];
|
|
70
|
+
/** `@format<TAB><format>`: 2 ({@link DECIDE_SNAPSHOT_FORMAT}) unless the writer passes 3 for a snapshot with `@rule` records. */
|
|
71
|
+
function encodeSnapshotFormatRecord(format = exports.DECIDE_SNAPSHOT_FORMAT) {
|
|
72
|
+
return `${exports.SNAPSHOT_FORMAT_RECORD_TAG}\t${format}`;
|
|
73
|
+
}
|
|
74
|
+
/** The format a `@format` line names: a positive integer, or null for any other line or a damaged one. */
|
|
75
|
+
function decodeSnapshotFormatRecord(line) {
|
|
76
|
+
const f = line.split('\t');
|
|
77
|
+
if (f.length !== 2 || f[0] !== exports.SNAPSHOT_FORMAT_RECORD_TAG || !/^[1-9][0-9]{0,5}$/.test(f[1]))
|
|
78
|
+
return null;
|
|
79
|
+
return Number(f[1]);
|
|
80
|
+
}
|
|
81
|
+
/** `ruleRef`: `<sopId>#<rule id>`. */
|
|
82
|
+
function decideRuleRef(sopId, ruleId) {
|
|
83
|
+
return `${sopId}#${ruleId}`;
|
|
84
|
+
}
|
|
85
|
+
/** `fallbackId`: `decide-fallback:<rule id>@<first 12 hex of sha256(sopId)>`. `sha256Hex` hashes a string's UTF-8 bytes. */
|
|
86
|
+
function decideFallbackId(ruleId, sopId, sha256Hex) {
|
|
87
|
+
return `${exports.DECIDE_FALLBACK_PREFIX}${ruleId}@${sha256Hex(sopId).slice(0, 12)}`;
|
|
88
|
+
}
|
|
89
|
+
/** What an older gate says when a fallback line refuses. The writer scrubs and bounds it like any reason. */
|
|
90
|
+
function decideFallbackReason(ruleId, sopTitle) {
|
|
91
|
+
return `Decide rule ${ruleId} (${sopTitle}): upgrade to Intutic 3.0 to evaluate this rule`;
|
|
92
|
+
}
|
|
93
|
+
/** A SOP body as a record carries it: its first {@link DECIDE_RULE_TEXT_MAX_BYTES} bytes of UTF-8, never splitting a character. */
|
|
94
|
+
function decideRuleText(body) {
|
|
95
|
+
let bytes = 0;
|
|
96
|
+
let end = 0;
|
|
97
|
+
for (const ch of body) {
|
|
98
|
+
const cp = ch.codePointAt(0);
|
|
99
|
+
const n = cp < 0x80 ? 1 : cp < 0x800 ? 2 : cp < 0x10000 ? 3 : 4;
|
|
100
|
+
if (bytes + n > exports.DECIDE_RULE_TEXT_MAX_BYTES)
|
|
101
|
+
break;
|
|
102
|
+
bytes += n;
|
|
103
|
+
end += ch.length;
|
|
104
|
+
}
|
|
105
|
+
return body.slice(0, end);
|
|
106
|
+
}
|
|
107
|
+
function decideIsObject(v) {
|
|
108
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
109
|
+
}
|
|
110
|
+
function decideIsNumber(v) {
|
|
111
|
+
return typeof v === 'number' && isFinite(v);
|
|
112
|
+
}
|
|
113
|
+
/** Every own key is one of `allowed`. */
|
|
114
|
+
function decideOnlyFields(obj, allowed) {
|
|
115
|
+
return Object.keys(obj).every((k) => allowed.indexOf(k) !== -1);
|
|
116
|
+
}
|
|
117
|
+
function decideNumberMap(v) {
|
|
118
|
+
return decideIsObject(v) && Object.keys(v).every((k) => decideIsNumber(v[k]));
|
|
119
|
+
}
|
|
120
|
+
/** JSON with keys sorted at every level and no whitespace, so a record is byte-stable. */
|
|
121
|
+
function decideCanonicalRecordJson(v) {
|
|
122
|
+
if (Array.isArray(v))
|
|
123
|
+
return '[' + v.map(decideCanonicalRecordJson).join(',') + ']';
|
|
124
|
+
if (decideIsObject(v)) {
|
|
125
|
+
return '{' + Object.keys(v).sort().map((k) => JSON.stringify(k) + ':' + decideCanonicalRecordJson(v[k])).join(',') + '}';
|
|
126
|
+
}
|
|
127
|
+
return JSON.stringify(v);
|
|
128
|
+
}
|
|
129
|
+
function decideToBase64(text) {
|
|
130
|
+
let binary = '';
|
|
131
|
+
for (const byte of new TextEncoder().encode(text))
|
|
132
|
+
binary += String.fromCharCode(byte);
|
|
133
|
+
return btoa(binary);
|
|
134
|
+
}
|
|
135
|
+
/** Standard base64 back to UTF-8 text. Throws on anything but the canonical encoding of valid UTF-8. */
|
|
136
|
+
function decideFromBase64(b64) {
|
|
137
|
+
const text = new TextDecoder('utf-8', { fatal: true }).decode(Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)));
|
|
138
|
+
if (decideToBase64(text) !== b64)
|
|
139
|
+
throw new Error('not canonical base64');
|
|
140
|
+
return text;
|
|
141
|
+
}
|
|
142
|
+
/** A pin as a record carries it, or null. Unknown fields refuse: a pin a reader cannot apply must not be read as a weaker one. */
|
|
143
|
+
function parseDecidePin(value) {
|
|
144
|
+
if (!decideIsObject(value) || !decideOnlyFields(value, DECIDE_PIN_FIELDS))
|
|
145
|
+
return null;
|
|
146
|
+
const { modelDigest, evalRunId, source, temperature, band, combiner } = value;
|
|
147
|
+
if (typeof modelDigest !== 'string' || modelDigest === '' || typeof evalRunId !== 'string' || evalRunId === '')
|
|
148
|
+
return null;
|
|
149
|
+
if (source !== 'local' && source !== 'server')
|
|
150
|
+
return null;
|
|
151
|
+
if (temperature !== undefined && !decideNumberMap(temperature))
|
|
152
|
+
return null;
|
|
153
|
+
if (band !== undefined) {
|
|
154
|
+
if (!decideIsObject(band) || !decideOnlyFields(band, ['yes_at', 'no_below']))
|
|
155
|
+
return null;
|
|
156
|
+
const { yes_at, no_below } = band;
|
|
157
|
+
if (!decideIsNumber(yes_at) || !decideIsNumber(no_below) || !(yes_at > 0.5 && yes_at < 1 && no_below > 0 && no_below < yes_at))
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
if (combiner !== undefined) {
|
|
161
|
+
if (!decideIsObject(combiner) || !decideOnlyFields(combiner, ['bias', 'weights']))
|
|
162
|
+
return null;
|
|
163
|
+
if (!decideIsNumber(combiner.bias) || !decideNumberMap(combiner.weights))
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
return value;
|
|
167
|
+
}
|
|
168
|
+
/** The record's tools column: the rule's tools, distinct and sorted, joined by commas. */
|
|
169
|
+
function decideToolsColumn(ir) {
|
|
170
|
+
return (ir.tools ?? []).filter((t, i, all) => all.indexOf(t) === i).sort().join(',');
|
|
171
|
+
}
|
|
172
|
+
/** The record as one `.rules` line. */
|
|
173
|
+
function encodeDecideRecord(record) {
|
|
174
|
+
return [
|
|
175
|
+
exports.DECIDE_RECORD_TAG,
|
|
176
|
+
record.fallbackId ?? '-',
|
|
177
|
+
record.rollout,
|
|
178
|
+
decideToolsColumn(record.ir),
|
|
179
|
+
decideToBase64(decideCanonicalRecordJson(record)),
|
|
180
|
+
].join('\t');
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Reads an `@decide` line back. Null, and so the fallback line stays in
|
|
184
|
+
* force, for any other line or when: it does not have five columns; the
|
|
185
|
+
* base64 or JSON is damaged; the payload has a field this reader does not
|
|
186
|
+
* know or lacks one; `check` finds the rule invalid or its hash differs from
|
|
187
|
+
* `ruleHash`; the stamp is inconsistent (only a rule stamped `enforce` by
|
|
188
|
+
* `rule` has a fallback line, and it is named for the rule); or columns 2 to
|
|
189
|
+
* 4 disagree with the payload.
|
|
190
|
+
*/
|
|
191
|
+
function decodeDecideRecord(line, check) {
|
|
192
|
+
const f = line.split('\t');
|
|
193
|
+
if (f.length !== 5 || f[0] !== exports.DECIDE_RECORD_TAG || !f[4])
|
|
194
|
+
return null;
|
|
195
|
+
let r;
|
|
196
|
+
try {
|
|
197
|
+
r = JSON.parse(decideFromBase64(f[4]));
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
if (!decideIsObject(r) || !decideOnlyFields(r, DECIDE_RECORD_FIELDS))
|
|
203
|
+
return null;
|
|
204
|
+
if (DECIDE_RECORD_FIELDS.some((k) => !Object.prototype.hasOwnProperty.call(r, k)))
|
|
205
|
+
return null;
|
|
206
|
+
const ir = r.ir;
|
|
207
|
+
if (r.v !== 1 || !decideIsObject(ir) || typeof ir.id !== 'string')
|
|
208
|
+
return null;
|
|
209
|
+
if (typeof r.ruleHash !== 'string' || !/^[0-9a-f]{64}$/.test(r.ruleHash) || check(ir) !== r.ruleHash)
|
|
210
|
+
return null;
|
|
211
|
+
if (typeof r.ref !== 'string' || r.ref.length <= ir.id.length + 1 || r.ref.slice(-(ir.id.length + 1)) !== '#' + ir.id)
|
|
212
|
+
return null;
|
|
213
|
+
if (r.rollout !== 'shadow' && r.rollout !== 'enforce')
|
|
214
|
+
return null;
|
|
215
|
+
if (typeof r.stampedBy !== 'string' || DECIDE_STAMPS.indexOf(r.stampedBy) === -1)
|
|
216
|
+
return null;
|
|
217
|
+
if (r.rollout === 'enforce') {
|
|
218
|
+
if (r.stampedBy !== 'rule' || typeof r.fallbackId !== 'string')
|
|
219
|
+
return null;
|
|
220
|
+
const head = exports.DECIDE_FALLBACK_PREFIX + ir.id + '@';
|
|
221
|
+
if (r.fallbackId.slice(0, head.length) !== head || !/^[0-9a-f]{12}$/.test(r.fallbackId.slice(head.length)))
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
else if (r.fallbackId !== null) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
if (typeof r.sopTitle !== 'string' || /[\t\r\n]/.test(r.sopTitle) || Array.from(r.sopTitle).length > exports.DECIDE_SOP_TITLE_MAX_CHARS)
|
|
228
|
+
return null;
|
|
229
|
+
if (typeof r.ruleText !== 'string' || new TextEncoder().encode(r.ruleText).length > exports.DECIDE_RULE_TEXT_MAX_BYTES)
|
|
230
|
+
return null;
|
|
231
|
+
if (!Array.isArray(r.pins) || r.pins.some((p) => parseDecidePin(p) === null))
|
|
232
|
+
return null;
|
|
233
|
+
const record = r;
|
|
234
|
+
if (f[1] !== (record.fallbackId ?? '-') || f[2] !== record.rollout || f[3] !== decideToolsColumn(record.ir))
|
|
235
|
+
return null;
|
|
236
|
+
return record;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* The decoders above as JavaScript source, for the hook gates to embed. The
|
|
240
|
+
* gate defines `check` with the embedded `./decide.ts` source and its own
|
|
241
|
+
* SHA-256. A CommonJS build names an exported constant `exports.X` inside a
|
|
242
|
+
* function; the source defines each as a plain `X`, so that prefix is dropped.
|
|
243
|
+
*/
|
|
244
|
+
exports.DECIDE_RECORD_JS_SOURCE = [
|
|
245
|
+
`const SNAPSHOT_FORMAT_RECORD_TAG = ${JSON.stringify(exports.SNAPSHOT_FORMAT_RECORD_TAG)};`,
|
|
246
|
+
`const DECIDE_RECORD_TAG = ${JSON.stringify(exports.DECIDE_RECORD_TAG)};`,
|
|
247
|
+
`const DECIDE_FALLBACK_PREFIX = ${JSON.stringify(exports.DECIDE_FALLBACK_PREFIX)};`,
|
|
248
|
+
`const DECIDE_RULE_TEXT_MAX_BYTES = ${exports.DECIDE_RULE_TEXT_MAX_BYTES};`,
|
|
249
|
+
`const DECIDE_SOP_TITLE_MAX_CHARS = ${exports.DECIDE_SOP_TITLE_MAX_CHARS};`,
|
|
250
|
+
`const DECIDE_STAMPS = ${JSON.stringify(DECIDE_STAMPS)};`,
|
|
251
|
+
`const DECIDE_RECORD_FIELDS = ${JSON.stringify(DECIDE_RECORD_FIELDS)};`,
|
|
252
|
+
`const DECIDE_PIN_FIELDS = ${JSON.stringify(DECIDE_PIN_FIELDS)};`,
|
|
253
|
+
...[
|
|
254
|
+
decodeSnapshotFormatRecord,
|
|
255
|
+
decideIsObject,
|
|
256
|
+
decideIsNumber,
|
|
257
|
+
decideOnlyFields,
|
|
258
|
+
decideNumberMap,
|
|
259
|
+
decideToBase64,
|
|
260
|
+
decideFromBase64,
|
|
261
|
+
parseDecidePin,
|
|
262
|
+
decideToolsColumn,
|
|
263
|
+
decodeDecideRecord,
|
|
264
|
+
].map((fn) => fn.toString().replace(/\bexports\./g, '')),
|
|
265
|
+
].join('\n');
|
|
266
|
+
//# sourceMappingURL=decideRecord.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decideRecord.js","sourceRoot":"","sources":["../src/decideRecord.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `decide(state, questions, opts)` for people building their own agents: ask
|
|
3
|
+
* a self-hosted model a few narrow questions about a state, and get each
|
|
4
|
+
* answer's probability, its three-valued reading through a band and, with a
|
|
5
|
+
* `when`, the outcome a decide rule would reach.
|
|
6
|
+
*
|
|
7
|
+
* The questions are written as a decide rule's `questions` and validated
|
|
8
|
+
* the same way, so wording that asks the model for approval is refused. Every
|
|
9
|
+
* call goes through the engine client and its host guard: a hosted decision
|
|
10
|
+
* API is refused before anything is sent, and there is no way to pass an
|
|
11
|
+
* engine object that skips the guard.
|
|
12
|
+
*
|
|
13
|
+
* Errors are {@link DecideEngineError}s and are thrown, so a caller cannot
|
|
14
|
+
* read an unavailable engine as an answer. The one exception is
|
|
15
|
+
* `too_large`: a state over the limit is never truncated, every answer is
|
|
16
|
+
* unknown, and with a `when` the outcome escalates.
|
|
17
|
+
*
|
|
18
|
+
* Self-contained but for the decide modules beside it, so `@intutic/gate` and
|
|
19
|
+
* `@intutic/clawde` carry byte-identical copies; each wraps it in a `decide()`
|
|
20
|
+
* that reads `~/.intutic/decide/engine.json` and the process environment. The
|
|
21
|
+
* Python port is `intutic_clawde/gate/decide_sdk.py`. All three run
|
|
22
|
+
* `fixtures/decide-sdk-vectors.json`.
|
|
23
|
+
*
|
|
24
|
+
* @module
|
|
25
|
+
*/
|
|
26
|
+
import { type ChoiceAtom, type DecideBand, type DecideQuestion, type Tri } from './decide.js';
|
|
27
|
+
import { type DecideAdapter, type DecideEngineConfig } from './decideEngine.js';
|
|
28
|
+
/** The budget when neither `budgetMs` nor `INTUTIC_DECIDE_BUDGET_MS` sets one. */
|
|
29
|
+
export declare const DECIDE_SDK_DEFAULT_BUDGET_MS = 7300;
|
|
30
|
+
/** The engine configuration's path under the home directory. */
|
|
31
|
+
export declare const DECIDE_ENGINE_FILE_PATH: readonly [".intutic", "decide", "engine.json"];
|
|
32
|
+
export interface DecideSdkOptions {
|
|
33
|
+
/** A `when` over the questions. With it, the result carries the rule's outcome. */
|
|
34
|
+
when?: string;
|
|
35
|
+
/** The band for every question without its own (default `{yes_at: 0.85, no_below: 0.15}`). */
|
|
36
|
+
band?: DecideBand;
|
|
37
|
+
/** What an unknown `when` does (default `ask`). */
|
|
38
|
+
escalate?: 'ask' | 'review' | 'refuse';
|
|
39
|
+
/** The engine's configuration. Without it, `engineFile` and `env` give it. */
|
|
40
|
+
engine?: DecideEngineConfig;
|
|
41
|
+
/** The parsed `engine.json`, or null when there is none. */
|
|
42
|
+
engineFile?: unknown;
|
|
43
|
+
/** The environment, which overrides `engineFile`. */
|
|
44
|
+
env?: Record<string, string | undefined>;
|
|
45
|
+
/** Milliseconds for the whole call (default `INTUTIC_DECIDE_BUDGET_MS`, else 7,300). */
|
|
46
|
+
budgetMs?: number;
|
|
47
|
+
}
|
|
48
|
+
export type DecideSdkAnswer = {
|
|
49
|
+
type: 'yes_no';
|
|
50
|
+
p: number | null;
|
|
51
|
+
value: Tri;
|
|
52
|
+
} | {
|
|
53
|
+
type: 'choice';
|
|
54
|
+
perOrder: Array<Record<string, number>> | null;
|
|
55
|
+
/** Each option's atom `q == o`: the mean over the orders, their spread, and its value. */
|
|
56
|
+
options: Record<string, ChoiceAtom>;
|
|
57
|
+
/** The option whose atom is T, or null. At most one can be, because `yes_at` is above 0.5. */
|
|
58
|
+
choice: string | null;
|
|
59
|
+
};
|
|
60
|
+
export interface DecideSdkResult {
|
|
61
|
+
answers: Record<string, DecideSdkAnswer>;
|
|
62
|
+
/** `when`'s value, or null without a `when`. */
|
|
63
|
+
value: Tri | null;
|
|
64
|
+
outcome: 'allow' | 'refuse' | 'escalate' | null;
|
|
65
|
+
code: 'DECIDE_REFUSED' | 'DECIDE_ESCALATED' | null;
|
|
66
|
+
via: 'ask' | 'review' | null;
|
|
67
|
+
/** `too_large` when the state was over 16,384 bytes, a tool call's limit, and nothing was asked. */
|
|
68
|
+
error: 'too_large' | null;
|
|
69
|
+
model: string | null;
|
|
70
|
+
modelDigest: string | null;
|
|
71
|
+
digestStrength: 'strong' | 'weak' | null;
|
|
72
|
+
adapter: DecideAdapter;
|
|
73
|
+
latencyMs: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* `engine.json`'s parsed content through `read`, or null when the file does
|
|
77
|
+
* not exist. A file that does not parse leaves the engine not configured.
|
|
78
|
+
*/
|
|
79
|
+
export declare function loadDecideEngineFile(read: (path: string) => string, path: string): unknown;
|
|
80
|
+
/**
|
|
81
|
+
* Asks `questions` about `state`. Throws a `TypeError` for questions (or a
|
|
82
|
+
* `when`) a decide rule would refuse, and a {@link DecideEngineError} when
|
|
83
|
+
* the engine is not configured, its host is refused, or it cannot answer.
|
|
84
|
+
*/
|
|
85
|
+
export declare function decideQuestions(state: string, questions: Record<string, DecideQuestion>, opts?: DecideSdkOptions): Promise<DecideSdkResult>;
|
|
86
|
+
//# sourceMappingURL=decideSdk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decideSdk.d.ts","sourceRoot":"","sources":["../src/decideSdk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAQL,KAAK,UAAU,EAEf,KAAK,UAAU,EACf,KAAK,cAAc,EAEnB,KAAK,GAAG,EACT,MAAM,aAAa,CAAA;AACpB,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,kBAAkB,EAExB,MAAM,mBAAmB,CAAA;AAE1B,kFAAkF;AAClF,eAAO,MAAM,4BAA4B,OAAQ,CAAA;AACjD,gEAAgE;AAChE,eAAO,MAAM,uBAAuB,gDAAiD,CAAA;AAErF,MAAM,WAAW,gBAAgB;IAC/B,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8FAA8F;IAC9F,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAA;IACtC,8EAA8E;IAC9E,MAAM,CAAC,EAAE,kBAAkB,CAAA;IAC3B,4DAA4D;IAC5D,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,qDAAqD;IACrD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IACxC,wFAAwF;IACxF,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,GAAG,CAAA;CAAE,GAChD;IACE,IAAI,EAAE,QAAQ,CAAA;IACd,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,IAAI,CAAA;IAC9C,0FAA0F;IAC1F,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACnC,8FAA8F;IAC9F,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB,CAAA;AAEL,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IACxC,gDAAgD;IAChD,KAAK,EAAE,GAAG,GAAG,IAAI,CAAA;IACjB,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,IAAI,CAAA;IAC/C,IAAI,EAAE,gBAAgB,GAAG,kBAAkB,GAAG,IAAI,CAAA;IAClD,GAAG,EAAE,KAAK,GAAG,QAAQ,GAAG,IAAI,CAAA;IAC5B,oGAAoG;IACpG,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,cAAc,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAA;IACxC,OAAO,EAAE,aAAa,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB;AA2BD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAa1F;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EACzC,IAAI,GAAE,gBAAqB,GAC1B,OAAO,CAAC,eAAe,CAAC,CA0D1B"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `decide(state, questions, opts)` for people building their own agents: ask
|
|
4
|
+
* a self-hosted model a few narrow questions about a state, and get each
|
|
5
|
+
* answer's probability, its three-valued reading through a band and, with a
|
|
6
|
+
* `when`, the outcome a decide rule would reach.
|
|
7
|
+
*
|
|
8
|
+
* The questions are written as a decide rule's `questions` and validated
|
|
9
|
+
* the same way, so wording that asks the model for approval is refused. Every
|
|
10
|
+
* call goes through the engine client and its host guard: a hosted decision
|
|
11
|
+
* API is refused before anything is sent, and there is no way to pass an
|
|
12
|
+
* engine object that skips the guard.
|
|
13
|
+
*
|
|
14
|
+
* Errors are {@link DecideEngineError}s and are thrown, so a caller cannot
|
|
15
|
+
* read an unavailable engine as an answer. The one exception is
|
|
16
|
+
* `too_large`: a state over the limit is never truncated, every answer is
|
|
17
|
+
* unknown, and with a `when` the outcome escalates.
|
|
18
|
+
*
|
|
19
|
+
* Self-contained but for the decide modules beside it, so `@intutic/gate` and
|
|
20
|
+
* `@intutic/clawde` carry byte-identical copies; each wraps it in a `decide()`
|
|
21
|
+
* that reads `~/.intutic/decide/engine.json` and the process environment. The
|
|
22
|
+
* Python port is `intutic_clawde/gate/decide_sdk.py`. All three run
|
|
23
|
+
* `fixtures/decide-sdk-vectors.json`.
|
|
24
|
+
*
|
|
25
|
+
* @module
|
|
26
|
+
*/
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.DECIDE_ENGINE_FILE_PATH = exports.DECIDE_SDK_DEFAULT_BUDGET_MS = void 0;
|
|
29
|
+
exports.loadDecideEngineFile = loadDecideEngineFile;
|
|
30
|
+
exports.decideQuestions = decideQuestions;
|
|
31
|
+
const decide_js_1 = require("./decide.js");
|
|
32
|
+
const decideEngine_js_1 = require("./decideEngine.js");
|
|
33
|
+
/** The budget when neither `budgetMs` nor `INTUTIC_DECIDE_BUDGET_MS` sets one. */
|
|
34
|
+
exports.DECIDE_SDK_DEFAULT_BUDGET_MS = 7_300;
|
|
35
|
+
/** The engine configuration's path under the home directory. */
|
|
36
|
+
exports.DECIDE_ENGINE_FILE_PATH = ['.intutic', 'decide', 'engine.json'];
|
|
37
|
+
/**
|
|
38
|
+
* The rule the questions are checked as: an `on: response` rule whose `when`
|
|
39
|
+
* is the caller's, or one naming every question once.
|
|
40
|
+
*/
|
|
41
|
+
function sdkRule(questions, opts) {
|
|
42
|
+
const when = opts.when ??
|
|
43
|
+
Object.keys(questions)
|
|
44
|
+
.sort()
|
|
45
|
+
.map((n) => (questions[n]?.type === 'choice' ? `${n} == ${decide_js_1.NONE_OF_THESE}` : n))
|
|
46
|
+
.join(' OR ');
|
|
47
|
+
const rule = { id: 'sdk', on: 'response', questions, when };
|
|
48
|
+
if (opts.band !== undefined)
|
|
49
|
+
rule.band = opts.band;
|
|
50
|
+
if (opts.escalate !== undefined)
|
|
51
|
+
rule.escalate = opts.escalate;
|
|
52
|
+
return rule;
|
|
53
|
+
}
|
|
54
|
+
function budgetFrom(opts, env) {
|
|
55
|
+
if (opts.budgetMs !== undefined)
|
|
56
|
+
return opts.budgetMs;
|
|
57
|
+
const raw = env.INTUTIC_DECIDE_BUDGET_MS?.trim();
|
|
58
|
+
if (!raw)
|
|
59
|
+
return exports.DECIDE_SDK_DEFAULT_BUDGET_MS;
|
|
60
|
+
if (!/^[0-9]+$/.test(raw))
|
|
61
|
+
throw new decideEngine_js_1.DecideEngineError('not_configured', 'INTUTIC_DECIDE_BUDGET_MS must be a whole number');
|
|
62
|
+
return Number(raw);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* `engine.json`'s parsed content through `read`, or null when the file does
|
|
66
|
+
* not exist. A file that does not parse leaves the engine not configured.
|
|
67
|
+
*/
|
|
68
|
+
function loadDecideEngineFile(read, path) {
|
|
69
|
+
let text;
|
|
70
|
+
try {
|
|
71
|
+
text = read(path);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
if (err.code === 'ENOENT')
|
|
75
|
+
return null;
|
|
76
|
+
throw new decideEngine_js_1.DecideEngineError('not_configured', `${path} cannot be read`);
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
return JSON.parse(text);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
throw new decideEngine_js_1.DecideEngineError('not_configured', `${path} is not JSON`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Asks `questions` about `state`. Throws a `TypeError` for questions (or a
|
|
87
|
+
* `when`) a decide rule would refuse, and a {@link DecideEngineError} when
|
|
88
|
+
* the engine is not configured, its host is refused, or it cannot answer.
|
|
89
|
+
*/
|
|
90
|
+
async function decideQuestions(state, questions, opts = {}) {
|
|
91
|
+
if (typeof state !== 'string')
|
|
92
|
+
throw new TypeError('decide: state must be a string');
|
|
93
|
+
const v = (0, decide_js_1.validateDecideRule)(sdkRule(questions, opts));
|
|
94
|
+
if (!v.ok)
|
|
95
|
+
throw new TypeError(v.reason);
|
|
96
|
+
const rule = v.rule;
|
|
97
|
+
const env = opts.env ?? {};
|
|
98
|
+
const cfg = opts.engine ?? (0, decideEngine_js_1.resolveDecideEngineConfig)(opts.engineFile ?? null, env);
|
|
99
|
+
const budgetMs = budgetFrom(opts, env);
|
|
100
|
+
const engine = (0, decideEngine_js_1.createDecideEngine)(cfg);
|
|
101
|
+
const names = Object.keys(rule.questions).sort();
|
|
102
|
+
const calls = names.map((name) => {
|
|
103
|
+
const q = rule.questions[name];
|
|
104
|
+
return q.type === 'choice' ? { name, type: 'choice', ask: q.ask, options: (0, decide_js_1.decideOptions)(q) } : { name, type: 'yes_no', ask: q.ask };
|
|
105
|
+
});
|
|
106
|
+
const started = Date.now();
|
|
107
|
+
let res = null;
|
|
108
|
+
// A caller's state is held to the tool-call limit: the engine takes larger
|
|
109
|
+
// ones, up to an `on: response` state's limit, from the state builders only.
|
|
110
|
+
if (new TextEncoder().encode(state).length <= decideEngine_js_1.DECIDE_MAX_STATE_BYTES) {
|
|
111
|
+
try {
|
|
112
|
+
res = await engine.ask(state, calls, started + budgetMs);
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
if (!(err instanceof decideEngine_js_1.DecideEngineError) || err.code !== 'too_large')
|
|
116
|
+
throw err;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const inputs = {};
|
|
120
|
+
const answers = {};
|
|
121
|
+
for (const name of names) {
|
|
122
|
+
const q = rule.questions[name];
|
|
123
|
+
const a = res?.answers[name];
|
|
124
|
+
const band = (0, decide_js_1.decideQuestionBand)(rule, name);
|
|
125
|
+
inputs[name] = { p: a?.p, perOrder: a?.perOrder };
|
|
126
|
+
if (q.type === 'yes_no') {
|
|
127
|
+
const p = typeof a?.p === 'number' ? a.p : null;
|
|
128
|
+
answers[name] = { type: 'yes_no', p, value: p === null ? 'U' : (0, decide_js_1.bandValue)(p, band) };
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
const options = (0, decide_js_1.choiceAtoms)((0, decide_js_1.decideOptions)(q), a?.perOrder ?? [], band);
|
|
132
|
+
const choice = Object.keys(options).find((o) => options[o].value === 'T') ?? null;
|
|
133
|
+
answers[name] = { type: 'choice', perOrder: a?.perOrder ?? null, options, choice };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const ruled = opts.when === undefined ? null : (0, decide_js_1.decideRuleOutcome)(rule, v.expr, inputs);
|
|
137
|
+
return {
|
|
138
|
+
answers,
|
|
139
|
+
value: ruled?.value ?? null,
|
|
140
|
+
outcome: ruled?.outcome ?? null,
|
|
141
|
+
code: ruled?.code ?? null,
|
|
142
|
+
via: ruled?.via ?? null,
|
|
143
|
+
error: res ? null : 'too_large',
|
|
144
|
+
model: res?.model ?? null,
|
|
145
|
+
modelDigest: res?.modelDigest ?? null,
|
|
146
|
+
digestStrength: res?.digestStrength ?? null,
|
|
147
|
+
adapter: cfg.adapter,
|
|
148
|
+
latencyMs: res?.latencyMs ?? Date.now() - started,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=decideSdk.js.map
|