@miadi/connector 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -3
- package/dist/approve.d.ts +8 -1
- package/dist/approve.js +39 -7
- package/dist/decisions.d.ts +4 -2
- package/dist/decisions.js +18 -9
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/mission-status.d.ts +121 -15
- package/dist/mission-status.js +199 -27
- package/dist/model-preflight.d.ts +144 -0
- package/dist/model-preflight.js +323 -0
- package/dist/providers.d.ts +29 -3
- package/dist/providers.js +32 -11
- package/dist/queue-state.js +2 -2
- package/dist/records.d.ts +13 -4
- package/dist/records.js +11 -3
- package/dist/request-state.d.ts +9 -1
- package/dist/request-state.js +37 -1
- package/dist/revisions.d.ts +110 -0
- package/dist/revisions.js +245 -0
- package/dist/server.d.ts +7 -3
- package/dist/server.js +4 -1
- package/dist/tools.d.ts +13 -2
- package/dist/tools.js +299 -22
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/mission-status.js
CHANGED
|
@@ -1,27 +1,70 @@
|
|
|
1
1
|
import { steerAttempts, steeringGrantOf, checkGrant } from "./steer.js";
|
|
2
2
|
const ISSUE_REF = /^([\w.-]+)\/([\w.-]+)#(\d+)$/;
|
|
3
3
|
const SESSION_REF = /^session:([0-9a-f-]{36})$/;
|
|
4
|
-
|
|
4
|
+
const MISSION_KINDS = new Set(["run_request", "work_proposal"]);
|
|
5
|
+
const CLOSING_ENDINGS = new Set(["exit", "complete_then_exit"]);
|
|
6
|
+
export const RELEASE_FRESHNESS_MS = 7 * 24 * 3600_000;
|
|
7
|
+
const CLOCK_SKEW_MS = 5 * 60_000;
|
|
8
|
+
/** A release receipt whose target the connector can check itself. */
|
|
9
|
+
const SELF_TARGET = /miadi-connector/;
|
|
10
|
+
/** The issue a mission implements, and how that is known. */
|
|
11
|
+
export function primaryIssueOf(record) {
|
|
12
|
+
const declared = record.body?.primary_issue;
|
|
13
|
+
if (typeof declared === "string" && ISSUE_REF.test(declared))
|
|
14
|
+
return { ref: declared, provenance: "declared" };
|
|
15
|
+
const first = record.refs.find((r) => ISSUE_REF.test(r));
|
|
16
|
+
return first ? { ref: first, provenance: "first_issue_ref" } : undefined;
|
|
17
|
+
}
|
|
18
|
+
const newestFirst = (a, b) => b.created_at.localeCompare(a.created_at);
|
|
19
|
+
function candidate(r, relation, provenance) {
|
|
20
|
+
const primary = primaryIssueOf(r);
|
|
21
|
+
return { id: r.id, kind: r.kind, created_at: r.created_at, relation, provenance, ...(primary ? { primary_issue: primary.ref } : {}) };
|
|
22
|
+
}
|
|
23
|
+
/** Whether a resolved reference may be acted on: an exact id, or a declared primary nobody else holds. */
|
|
24
|
+
export function authorizesActions(found) {
|
|
25
|
+
return found.resolution === "exact" || (found.resolution === "declared_primary" && !found.contested);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The record a mission reference resolves to, how it was found, and every other
|
|
29
|
+
* candidate. `record` is absent when the reference is ambiguous or only related.
|
|
30
|
+
* Expects the folded ledger (`current()`), so a superseding record never stands
|
|
31
|
+
* as a mission of its own.
|
|
32
|
+
*/
|
|
5
33
|
export function resolveMissionSubject(ref, records) {
|
|
6
34
|
const exact = records.find((r) => r.id === ref);
|
|
7
35
|
if (exact)
|
|
8
|
-
return { record: exact, via: "record_id" };
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
36
|
+
return { record: exact, via: "record_id", resolution: "exact", candidates: [] };
|
|
37
|
+
// A request retired as an earlier version of another (jgwill/Miadi#919) is not a mission of its own.
|
|
38
|
+
const missions = records.filter((r) => MISSION_KINDS.has(r.kind) && r.completion_receipt?.decision !== "superseded").sort(newestFirst);
|
|
39
|
+
if (SESSION_REF.test(ref)) {
|
|
40
|
+
const citing = missions.filter((r) => r.refs.includes(ref));
|
|
41
|
+
if (!citing.length)
|
|
42
|
+
return null;
|
|
43
|
+
if (citing.length === 1)
|
|
44
|
+
return { record: citing[0], via: "session_ref", resolution: "inferred_primary", candidates: [candidate(citing[0], "cited", "cited_ref")] };
|
|
45
|
+
return { via: "session_ref", resolution: "ambiguous_mission", candidates: citing.map((r) => candidate(r, "cited", "cited_ref")) };
|
|
17
46
|
}
|
|
18
47
|
if (ISSUE_REF.test(ref)) {
|
|
19
|
-
const citing =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
48
|
+
const citing = missions.filter((r) => r.refs.includes(ref) || r.evidence_refs.includes(ref));
|
|
49
|
+
if (!citing.length)
|
|
50
|
+
return null;
|
|
51
|
+
const cands = citing.map((r) => {
|
|
52
|
+
const primary = primaryIssueOf(r);
|
|
53
|
+
return primary?.ref === ref ? candidate(r, "primary", primary.provenance) : candidate(r, "related", "cited_ref");
|
|
54
|
+
});
|
|
55
|
+
const declared = cands.filter((c) => c.relation === "primary" && c.provenance === "declared");
|
|
56
|
+
const inferred = cands.filter((c) => c.relation === "primary" && c.provenance === "first_issue_ref");
|
|
57
|
+
const pick = (c) => citing.find((r) => r.id === c.id);
|
|
58
|
+
if (declared.length === 1) {
|
|
59
|
+
return { record: pick(declared[0]), via: "issue_ref", resolution: "declared_primary", ...(inferred.length ? { contested: true } : {}), candidates: cands };
|
|
60
|
+
}
|
|
61
|
+
if (declared.length > 1)
|
|
62
|
+
return { via: "issue_ref", resolution: "ambiguous_mission", candidates: cands };
|
|
63
|
+
if (inferred.length === 1)
|
|
64
|
+
return { record: pick(inferred[0]), via: "issue_ref", resolution: "inferred_primary", candidates: cands };
|
|
65
|
+
if (inferred.length > 1)
|
|
66
|
+
return { via: "issue_ref", resolution: "ambiguous_mission", candidates: cands };
|
|
67
|
+
return { via: "issue_ref", resolution: "related_only", candidates: cands };
|
|
25
68
|
}
|
|
26
69
|
return null;
|
|
27
70
|
}
|
|
@@ -48,12 +91,108 @@ function nextAction(record, standing, queue) {
|
|
|
48
91
|
: "The request is done. A mission closes when its session's ending is decided, not when this request completes.";
|
|
49
92
|
}
|
|
50
93
|
}
|
|
94
|
+
const str = (v) => (typeof v === "string" ? v.trim() : "");
|
|
95
|
+
/** name@version from "npm:@scope/name@1.2.3" or "@scope/name@1.2.3". */
|
|
96
|
+
function artifactVersion(artifact) {
|
|
97
|
+
const m = /^(?:npm:)?((?:@[\w.-]+\/)?[\w.-]+)@(\d[\w.+-]*)$/.exec(artifact.trim());
|
|
98
|
+
return m ? { pkg: m[1], version: m[2] } : undefined;
|
|
99
|
+
}
|
|
100
|
+
/** Records that carry evidence about this mission: itself, then any record citing its id, newest first. */
|
|
101
|
+
function evidenceRecords(record, records) {
|
|
102
|
+
const citing = records.filter((r) => r.id !== record.id && (r.refs.includes(record.id) || r.evidence_refs.includes(record.id))).sort(newestFirst);
|
|
103
|
+
return [record, ...citing];
|
|
104
|
+
}
|
|
105
|
+
function releaseTruth(record, records, deps, now) {
|
|
106
|
+
let claimed = false;
|
|
107
|
+
for (const r of evidenceRecords(record, records)) {
|
|
108
|
+
const receipt = (r.completion_receipt ?? {});
|
|
109
|
+
const rel = (receipt.release ?? r.body?.release);
|
|
110
|
+
if (rel && typeof rel === "object") {
|
|
111
|
+
const ev = { artifact: str(rel.artifact), target: str(rel.target), verified_at: str(rel.verified_at), verification: str(rel.verification) };
|
|
112
|
+
const at = Date.parse(ev.verified_at);
|
|
113
|
+
if (!ev.artifact || !ev.target || !ev.verification || Number.isNaN(at) || at > now.getTime() + CLOCK_SKEW_MS) {
|
|
114
|
+
claimed = true;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const approver = str((record.completion_receipt ?? {}).decided_by?.person_id);
|
|
118
|
+
const evidence = { ...ev, record: r.id, by: r.actor.name, by_approver: Boolean(approver) && r.actor.person_id === approver };
|
|
119
|
+
const av = artifactVersion(ev.artifact);
|
|
120
|
+
const self = av && SELF_TARGET.test(ev.target) ? deps.liveVersionOf?.(av.pkg) : undefined;
|
|
121
|
+
if (av && self !== undefined) {
|
|
122
|
+
evidence.live_version = self;
|
|
123
|
+
if (self !== av.version)
|
|
124
|
+
return { released: "live_mismatch", release_evidence: evidence };
|
|
125
|
+
}
|
|
126
|
+
if (now.getTime() - at > (deps.releaseFreshnessMs ?? RELEASE_FRESHNESS_MS))
|
|
127
|
+
return { released: "stale", release_evidence: evidence };
|
|
128
|
+
return { released: self !== undefined ? "verified" : "receipt_recorded", release_evidence: evidence };
|
|
129
|
+
}
|
|
130
|
+
if (r.id === record.id && (receipt.published || receipt.version))
|
|
131
|
+
claimed = true;
|
|
132
|
+
}
|
|
133
|
+
return { released: claimed ? "claimed_unverified" : "no_evidence" };
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The approved ending that closes this mission: it names the mission in its own `mission` field (one ending,
|
|
137
|
+
* one mission; a citation in refs is not enough) and, when the mission names sessions, ends one of them.
|
|
138
|
+
*/
|
|
139
|
+
function closureTruth(record, records, missionSessions) {
|
|
140
|
+
const endings = records
|
|
141
|
+
.filter((r) => {
|
|
142
|
+
const b = (r.body ?? {});
|
|
143
|
+
if (r.kind !== "session_completion_proposal" || r.approval_state !== "approved" || b.mission !== record.id)
|
|
144
|
+
return false;
|
|
145
|
+
if (!CLOSING_ENDINGS.has(str(b.ending)))
|
|
146
|
+
return false;
|
|
147
|
+
return missionSessions.size === 0 || missionSessions.has(str(b.session_id));
|
|
148
|
+
})
|
|
149
|
+
.sort(newestFirst);
|
|
150
|
+
const e = endings[0];
|
|
151
|
+
if (!e)
|
|
152
|
+
return undefined;
|
|
153
|
+
const receipt = (e.completion_receipt ?? {});
|
|
154
|
+
const sessionId = str(e.body.session_id);
|
|
155
|
+
return {
|
|
156
|
+
record: e.id,
|
|
157
|
+
ending: str(e.body.ending),
|
|
158
|
+
session_id: sessionId,
|
|
159
|
+
session_bound: missionSessions.has(sessionId),
|
|
160
|
+
...(str(receipt.decided_by?.name) ? { decided_by: str(receipt.decided_by.name) } : {}),
|
|
161
|
+
...(str(receipt.decided_at) ? { decided_at: str(receipt.decided_at) } : {}),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
51
164
|
export async function missionStatus(ref, records, deps) {
|
|
52
165
|
const notes = [];
|
|
53
166
|
const found = resolveMissionSubject(ref, records);
|
|
54
167
|
if (!found)
|
|
55
168
|
return { ref, found: false, notes: ["No mission record resolves from this reference."] };
|
|
169
|
+
if (!found.record) {
|
|
170
|
+
const ambiguous = found.resolution === "ambiguous_mission";
|
|
171
|
+
return {
|
|
172
|
+
ref,
|
|
173
|
+
found: false,
|
|
174
|
+
via: found.via,
|
|
175
|
+
identity: { resolution: found.resolution, authorizes_actions: false, candidates: found.candidates },
|
|
176
|
+
next_authorized_action: "Name the mission by its record id. Nothing is steered, released or closed against this reference.",
|
|
177
|
+
notes: [
|
|
178
|
+
ambiguous
|
|
179
|
+
? "Several requests hold this reference as their primary mission; none is chosen for you, the newest included."
|
|
180
|
+
: "Requests cite this reference only as related; none implements it, so it names no mission.",
|
|
181
|
+
],
|
|
182
|
+
};
|
|
183
|
+
}
|
|
56
184
|
const { record, via } = found;
|
|
185
|
+
const primary = primaryIssueOf(record);
|
|
186
|
+
const identity = {
|
|
187
|
+
resolution: found.resolution,
|
|
188
|
+
...(found.contested ? { contested: true } : {}),
|
|
189
|
+
authorizes_actions: authorizesActions(found),
|
|
190
|
+
...(primary ? { primary_issue: primary } : {}),
|
|
191
|
+
candidates: found.candidates,
|
|
192
|
+
};
|
|
193
|
+
if (found.resolution === "inferred_primary") {
|
|
194
|
+
notes.push(`Resolved by inference (${via === "session_ref" ? "the only request citing this session" : "the first issue in the request's refs"}). Name ${record.id} before any steer, release or closure.`);
|
|
195
|
+
}
|
|
57
196
|
const standing = deps.projectOf(record) ?? undefined;
|
|
58
197
|
const queue = deps.queueOf?.(record);
|
|
59
198
|
const now = deps.now?.() ?? new Date();
|
|
@@ -64,7 +203,6 @@ export async function missionStatus(ref, records, deps) {
|
|
|
64
203
|
const named = String(record.body.session ?? "") || undefined;
|
|
65
204
|
const seen = new Set();
|
|
66
205
|
const addSession = (session, association) => {
|
|
67
|
-
const key = `${association}:${session}`;
|
|
68
206
|
if (seen.has(session))
|
|
69
207
|
return;
|
|
70
208
|
seen.add(session);
|
|
@@ -74,7 +212,6 @@ export async function missionStatus(ref, records, deps) {
|
|
|
74
212
|
live: live ? (live.has(session) ? "running" : "absent") : "unknown",
|
|
75
213
|
...(deps.paneBusy && live?.has(session) ? { activity: deps.paneBusy(session) } : {}),
|
|
76
214
|
});
|
|
77
|
-
void key;
|
|
78
215
|
};
|
|
79
216
|
if (typeof briefTo === "string" && briefTo)
|
|
80
217
|
addSession(briefTo.replace(/^new:/, ""), "brief_target");
|
|
@@ -91,14 +228,22 @@ export async function missionStatus(ref, records, deps) {
|
|
|
91
228
|
notes.push("Live session state was not joined (the viewer's tier or the host does not allow it).");
|
|
92
229
|
const issues = [];
|
|
93
230
|
if (deps.issueOf) {
|
|
94
|
-
for (const r of [...record.refs, ...record.evidence_refs]) {
|
|
231
|
+
for (const r of [...new Set([...record.refs, ...record.evidence_refs])]) {
|
|
95
232
|
const m = ISSUE_REF.exec(r);
|
|
96
233
|
if (!m)
|
|
97
234
|
continue;
|
|
98
|
-
const issue = await deps
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
235
|
+
const issue = await deps.issueOf(m[1], m[2], m[3]).catch(() => ({ state: "unverified", access: "unreachable" }));
|
|
236
|
+
issues.push({
|
|
237
|
+
ref: r,
|
|
238
|
+
relation: primary?.ref === r ? "primary" : "related",
|
|
239
|
+
state: issue.state,
|
|
240
|
+
...(issue.title ? { title: issue.title } : {}),
|
|
241
|
+
...(issue.merged !== undefined ? { merged: issue.merged } : {}),
|
|
242
|
+
...(issue.access ? { access: issue.access } : {}),
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
if (issues.some((i) => i.state === "unverified")) {
|
|
246
|
+
notes.push("Some GitHub references could not be read by the connector; they are unverified, not missing.");
|
|
102
247
|
}
|
|
103
248
|
}
|
|
104
249
|
const grant = steeringGrantOf(record);
|
|
@@ -108,22 +253,47 @@ export async function missionStatus(ref, records, deps) {
|
|
|
108
253
|
const receipt = (record.completion_receipt ?? {});
|
|
109
254
|
const anyLive = sessions.some((s) => s.live === "running");
|
|
110
255
|
const prs = issues.filter((i) => i.merged !== undefined);
|
|
256
|
+
const unverifiedRefs = issues.some((i) => i.state === "unverified");
|
|
257
|
+
const release = releaseTruth(record, records, deps, now);
|
|
258
|
+
const missionSessions = new Set(sessions.map((x) => x.session.replace(/^session:/, "")));
|
|
259
|
+
const closure = closureTruth(record, records, missionSessions);
|
|
260
|
+
const choice = standing?.decision?.choice ?? (str(receipt.decision) || undefined);
|
|
261
|
+
const instructions = standing?.decision?.instructions ?? (str(receipt.instructions) || undefined);
|
|
111
262
|
const stageTruth = {
|
|
112
263
|
approved: record.approval_state === "approved",
|
|
264
|
+
approval: {
|
|
265
|
+
state: record.approval_state ?? "not_required",
|
|
266
|
+
...(choice ? { choice } : {}),
|
|
267
|
+
...(instructions ? { instructions } : {}),
|
|
268
|
+
outstanding: standing?.outstanding ?? [],
|
|
269
|
+
},
|
|
113
270
|
queued: queue?.state === "queued" || queue?.state === "delivered",
|
|
114
271
|
delivered: queue?.state === "delivered",
|
|
115
272
|
running: anyLive ? "evidence" : "no_evidence",
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
273
|
+
request_completed: record.state === "completed",
|
|
274
|
+
pr_merged: unverifiedRefs ? "unverified" : prs.length ? (prs.every((p) => p.merged) ? "merged" : "open_or_absent") : "no_pr",
|
|
275
|
+
released: release.released,
|
|
276
|
+
...(release.release_evidence ? { release_evidence: release.release_evidence } : {}),
|
|
277
|
+
closed: Boolean(closure),
|
|
278
|
+
...(closure ? { closure_evidence: closure } : {}),
|
|
119
279
|
};
|
|
120
280
|
if (stageTruth.running === "no_evidence" && record.approval_state === "approved") {
|
|
121
281
|
notes.push("No bound session shows live; an idle or absent pane is not proof of completion or failure.");
|
|
122
282
|
}
|
|
283
|
+
if (stageTruth.request_completed && !stageTruth.closed) {
|
|
284
|
+
notes.push("The request completed; the mission stays open until an approved session ending cites it.");
|
|
285
|
+
}
|
|
286
|
+
if (stageTruth.pr_merged === "merged" && stageTruth.released !== "verified" && stageTruth.released !== "receipt_recorded") {
|
|
287
|
+
notes.push("Merged is not released: no fresh production check is recorded for this mission.");
|
|
288
|
+
}
|
|
289
|
+
if (found.contested) {
|
|
290
|
+
notes.push("This request declares the issue, and an earlier request holds it by inference; neither is acted on by issue. Name the record id.");
|
|
291
|
+
}
|
|
123
292
|
return {
|
|
124
293
|
ref,
|
|
125
294
|
found: true,
|
|
126
295
|
via,
|
|
296
|
+
identity,
|
|
127
297
|
subject: {
|
|
128
298
|
id: record.id,
|
|
129
299
|
kind: record.kind,
|
|
@@ -138,7 +308,9 @@ export async function missionStatus(ref, records, deps) {
|
|
|
138
308
|
issues,
|
|
139
309
|
steering: { grant: grantState, attempts_used: attempts, ...(grant?.max_attempts !== undefined ? { max_attempts: grant.max_attempts } : {}) },
|
|
140
310
|
stage_truth: stageTruth,
|
|
141
|
-
next_authorized_action:
|
|
311
|
+
next_authorized_action: identity.authorizes_actions
|
|
312
|
+
? nextAction(record, standing ?? null, queue)
|
|
313
|
+
: `Not authorizing (${found.contested ? "contested" : "inferred"}): call mission_status with ${record.id} before acting. ${nextAction(record, standing ?? null, queue)}`,
|
|
142
314
|
notes,
|
|
143
315
|
};
|
|
144
316
|
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { type JevConfig, type ProviderAvailability, type ProviderId } from "./providers.js";
|
|
2
|
+
export declare const PREFLIGHT_CONTRACT = "miadi.mission_model_preflight";
|
|
3
|
+
export declare const PREFLIGHT_VERSION = 1;
|
|
4
|
+
/** Below this, a Jev choice is not acted on (the ≥0.8 practice of jgwill/Miadi#770). */
|
|
5
|
+
export declare const JEV_CONFIDENCE_FLOOR = 0.8;
|
|
6
|
+
export declare const JEV_MODEL = "jev-latest";
|
|
7
|
+
export declare const TASK_TYPES: readonly ["file_relevance", "architecture_refactor", "implementation", "review"];
|
|
8
|
+
export type TaskType = (typeof TASK_TYPES)[number];
|
|
9
|
+
export declare const CRITICALITIES: readonly ["normal", "security_critical"];
|
|
10
|
+
export type Criticality = (typeof CRITICALITIES)[number];
|
|
11
|
+
export declare const REVIEW_TIERS: readonly ["standard", "stronger_reviewer", "human"];
|
|
12
|
+
export type ReviewTier = (typeof REVIEW_TIERS)[number];
|
|
13
|
+
export interface ModelCandidate {
|
|
14
|
+
/** provider, or provider:model when a model is known; the option Jev is asked to choose. */
|
|
15
|
+
option: string;
|
|
16
|
+
provider: ProviderId;
|
|
17
|
+
/** A concrete model id when the host shows one, else null. */
|
|
18
|
+
model_id: string | null;
|
|
19
|
+
/** configured_default: the provider's own config names it; it is not proof of what a launch will use. */
|
|
20
|
+
model_id_source: "configured_default" | "fixture" | "unknown";
|
|
21
|
+
/** What is known of the model's strength, with its source; absent on a live host, where nothing is measured yet. */
|
|
22
|
+
capability_note?: string;
|
|
23
|
+
installed: boolean;
|
|
24
|
+
version?: string;
|
|
25
|
+
/** Config-directory evidence only: never a verified sign-in. */
|
|
26
|
+
auth: "config_dir_only" | "none";
|
|
27
|
+
quota: "unknown";
|
|
28
|
+
eligible: boolean;
|
|
29
|
+
policy_reason: string;
|
|
30
|
+
}
|
|
31
|
+
export interface LaunchEvidence {
|
|
32
|
+
provider: ProviderId;
|
|
33
|
+
model?: string;
|
|
34
|
+
/** What shows it (a process line, a pane header); reported by the caller, not checked by the connector. */
|
|
35
|
+
evidence: string;
|
|
36
|
+
}
|
|
37
|
+
export type PreflightState = "recommended" | "abstained" | "provider_mismatch";
|
|
38
|
+
export type DeciderSource = "jev" | "static_fallback" | "synthetic_fixture";
|
|
39
|
+
export type RouterState = "answered" | "not_requested" | "router_unconfigured" | "router_unavailable" | "invalid_answer";
|
|
40
|
+
export interface ModelPreflight {
|
|
41
|
+
contract: typeof PREFLIGHT_CONTRACT;
|
|
42
|
+
version: typeof PREFLIGHT_VERSION;
|
|
43
|
+
state: PreflightState;
|
|
44
|
+
mission: {
|
|
45
|
+
id: string;
|
|
46
|
+
resolution: string;
|
|
47
|
+
authorizes_actions: boolean;
|
|
48
|
+
primary_issue?: string;
|
|
49
|
+
};
|
|
50
|
+
/** sha256 of the decision state (what Jev sees or would see), for audit and replay. */
|
|
51
|
+
input_hash: string;
|
|
52
|
+
task: {
|
|
53
|
+
type: TaskType;
|
|
54
|
+
criticality: Criticality;
|
|
55
|
+
};
|
|
56
|
+
approved_provider: ProviderId | null;
|
|
57
|
+
/** approved: the request was approved with this provider. requested: it is not approved (yet); the provider is only asked for. */
|
|
58
|
+
provider_basis: "approved" | "requested";
|
|
59
|
+
candidates: ModelCandidate[];
|
|
60
|
+
decider: {
|
|
61
|
+
source: DeciderSource;
|
|
62
|
+
router: RouterState;
|
|
63
|
+
model?: string;
|
|
64
|
+
calibration: "uncalibrated" | "not_applicable";
|
|
65
|
+
confidence_floor?: number;
|
|
66
|
+
derivation: string;
|
|
67
|
+
detail?: string;
|
|
68
|
+
};
|
|
69
|
+
recommendation: {
|
|
70
|
+
provider?: ProviderId;
|
|
71
|
+
model_id?: string | null;
|
|
72
|
+
confidence: number | null;
|
|
73
|
+
probabilities?: Record<string, number>;
|
|
74
|
+
review_tier: ReviewTier;
|
|
75
|
+
explanation: string;
|
|
76
|
+
abstain_reasons: string[];
|
|
77
|
+
};
|
|
78
|
+
evidence: {
|
|
79
|
+
latency_ms?: number;
|
|
80
|
+
jev_billing?: Record<string, string>;
|
|
81
|
+
model_cost: "unknown";
|
|
82
|
+
quota: "unknown";
|
|
83
|
+
};
|
|
84
|
+
launch: {
|
|
85
|
+
approved_provider: ProviderId | null;
|
|
86
|
+
actual: "unverified" | (LaunchEvidence & {
|
|
87
|
+
source: "reported";
|
|
88
|
+
});
|
|
89
|
+
mismatch: boolean | null;
|
|
90
|
+
};
|
|
91
|
+
policy: {
|
|
92
|
+
deterministic: true;
|
|
93
|
+
authorizes: false;
|
|
94
|
+
note: string;
|
|
95
|
+
};
|
|
96
|
+
activation_plan?: string[];
|
|
97
|
+
}
|
|
98
|
+
/** What a decider returns for one request: answers by question id, and what the call cost. */
|
|
99
|
+
export interface DeciderAnswer {
|
|
100
|
+
answers: Record<string, unknown>;
|
|
101
|
+
billing?: Record<string, string>;
|
|
102
|
+
latency_ms?: number;
|
|
103
|
+
}
|
|
104
|
+
export interface JevAdapter {
|
|
105
|
+
source: "jev" | "synthetic_fixture";
|
|
106
|
+
model: string;
|
|
107
|
+
decide(request: JevRequest): Promise<DeciderAnswer>;
|
|
108
|
+
}
|
|
109
|
+
export interface JevRequest {
|
|
110
|
+
state: Record<string, unknown>;
|
|
111
|
+
model: string;
|
|
112
|
+
questions: Record<string, {
|
|
113
|
+
type: "choice";
|
|
114
|
+
instructions: string;
|
|
115
|
+
criteria: Record<string, string>;
|
|
116
|
+
}>;
|
|
117
|
+
}
|
|
118
|
+
/** The model a provider's own config names, when it names one. Only that key is read. */
|
|
119
|
+
export declare function configuredModel(provider: ProviderId, home?: string): string | null;
|
|
120
|
+
export declare function candidatesFor(found: ProviderAvailability[], approved: ProviderId | null, modelOf?: (p: ProviderId) => string | null, basis?: "approved" | "requested"): ModelCandidate[];
|
|
121
|
+
/** The least review a task gets, whatever any decider says. Jev may raise it, never lower it. */
|
|
122
|
+
export declare function reviewFloor(task: TaskType, criticality: Criticality): ReviewTier;
|
|
123
|
+
export declare function decisionState(task: TaskType, criticality: Criticality, candidates: ModelCandidate[]): Record<string, unknown>;
|
|
124
|
+
export declare function hashState(state: unknown): string;
|
|
125
|
+
export declare function buildJevRequest(state: Record<string, unknown>, candidates: ModelCandidate[], model?: string): JevRequest;
|
|
126
|
+
/** A choice answer against its question: the failures, empty when it holds. */
|
|
127
|
+
export declare function checkChoice(criteria: Record<string, string>, answer: any): string[];
|
|
128
|
+
/** One call to Jev, never resent: a lost answer may already have been charged. */
|
|
129
|
+
export declare function jevAdapter(config: JevConfig, fetchImpl?: typeof fetch, timeoutMs?: number): JevAdapter;
|
|
130
|
+
export interface PreflightInput {
|
|
131
|
+
mission: ModelPreflight["mission"];
|
|
132
|
+
approved_provider: ProviderId | null;
|
|
133
|
+
provider_basis?: "approved" | "requested";
|
|
134
|
+
task: TaskType;
|
|
135
|
+
criticality: Criticality;
|
|
136
|
+
candidates: ModelCandidate[];
|
|
137
|
+
launch?: LaunchEvidence;
|
|
138
|
+
/** The decider to consult, when the caller asked for one; absent means static fallback only. */
|
|
139
|
+
adapter?: JevAdapter;
|
|
140
|
+
/** Why no adapter: not_requested or router_unconfigured. */
|
|
141
|
+
routerWhenAbsent?: "not_requested" | "router_unconfigured";
|
|
142
|
+
}
|
|
143
|
+
export declare const ACTIVATION_PLAN: string[];
|
|
144
|
+
export declare function modelPreflight(input: PreflightInput): Promise<ModelPreflight>;
|