pi-fabric 0.109.5 → 0.110.0
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/chunks/chunk-3R6NIF2N.js +844 -0
- package/dist/chunks/chunk-3R6NIF2N.js.map +7 -0
- package/dist/chunks/{chunk-N2VKKA7O.js → chunk-4WCEZL6Y.js} +2 -2
- package/dist/chunks/{chunk-K7LQIEJY.js → chunk-5DKVWEE4.js} +2 -2
- package/dist/chunks/{chunk-N7HJRZCL.js → chunk-6DFOHIDR.js} +15 -13
- package/dist/chunks/{chunk-N7HJRZCL.js.map → chunk-6DFOHIDR.js.map} +2 -2
- package/dist/chunks/{chunk-LQSICKDK.js → chunk-A5XF3ZPJ.js} +18 -1
- package/dist/chunks/chunk-A5XF3ZPJ.js.map +7 -0
- package/dist/chunks/{chunk-YKT5VLLQ.js → chunk-DSNR5U7X.js} +2 -2
- package/dist/chunks/{chunk-R2ZQTTLX.js → chunk-EB4DPHWY.js} +2 -2
- package/dist/chunks/chunk-GUZG4GGT.js +157 -0
- package/dist/chunks/chunk-GUZG4GGT.js.map +7 -0
- package/dist/chunks/{chunk-COQPGIBO.js → chunk-HZGQ7XYM.js} +2 -2
- package/dist/chunks/{chunk-7OFAO4HK.js → chunk-MOJBW5X2.js} +41 -2
- package/dist/chunks/chunk-MOJBW5X2.js.map +7 -0
- package/dist/chunks/{chunk-67RXNJLG.js → chunk-OC2P7EQ3.js} +4 -3
- package/dist/chunks/chunk-OC2P7EQ3.js.map +7 -0
- package/dist/chunks/{chunk-CZPQTISQ.js → chunk-T2CJJASQ.js} +5 -3
- package/dist/chunks/chunk-T2CJJASQ.js.map +7 -0
- package/dist/chunks/{chunk-OR3FVFRO.js → chunk-ZHT5YFY2.js} +2 -2
- package/dist/components/configuration.js +3 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/core/action-registry.d.ts.map +1 -1
- package/dist/core/action-registry.js +2 -2
- package/dist/core/action-result.d.ts +10 -0
- package/dist/core/action-result.d.ts.map +1 -1
- package/dist/extension-bootstrap.js +4 -4
- package/dist/extension.d.ts.map +1 -1
- package/dist/fabric-runtime-state.d.ts +2 -0
- package/dist/fabric-runtime-state.d.ts.map +1 -1
- package/dist/fabric-runtime-state.js +15 -7
- package/dist/fabric-runtime-state.js.map +2 -2
- package/dist/fabric-state.d.ts +2 -0
- package/dist/fabric-state.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/jev/config.d.ts +5 -0
- package/dist/jev/config.d.ts.map +1 -1
- package/dist/jev/decision-profiles.d.ts +8 -0
- package/dist/jev/decision-profiles.d.ts.map +1 -0
- package/dist/jev/decision-schema.d.ts +35 -0
- package/dist/jev/decision-schema.d.ts.map +1 -0
- package/dist/jev/decision-types.d.ts +139 -0
- package/dist/jev/decision-types.d.ts.map +1 -0
- package/dist/jev/decisions.d.ts +27 -0
- package/dist/jev/decisions.d.ts.map +1 -0
- package/dist/jev/guest-types.d.ts +1 -1
- package/dist/jev/guest-types.d.ts.map +1 -1
- package/dist/jev/manager.d.ts +7 -0
- package/dist/jev/manager.d.ts.map +1 -1
- package/dist/jev/observation.js +2 -2
- package/dist/jev/types.d.ts +3 -2
- package/dist/jev/types.d.ts.map +1 -1
- package/dist/jev-fabric/resolve.d.ts +4 -0
- package/dist/jev-fabric/resolve.d.ts.map +1 -1
- package/dist/jev-fabric/resolve.js +1 -0
- package/dist/jev-fabric/resolve.js.map +2 -2
- package/dist/jev-fabric/serve.d.ts +5 -15
- package/dist/jev-fabric/serve.d.ts.map +1 -1
- package/dist/jev-fabric/serve.js +87 -56
- package/dist/jev-fabric/serve.js.map +2 -2
- package/dist/jev.d.ts +4 -0
- package/dist/jev.d.ts.map +1 -1
- package/dist/jev.js +20 -6
- package/dist/memory/extractive-history.d.ts +3 -0
- package/dist/memory/extractive-history.d.ts.map +1 -1
- package/dist/memory/extractive-history.js +36 -7
- package/dist/memory/extractive-history.js.map +3 -3
- package/dist/memory/extractive-index.d.ts.map +1 -1
- package/dist/providers/jev-provider.d.ts +2 -0
- package/dist/providers/jev-provider.d.ts.map +1 -1
- package/dist/providers/jev-provider.js +5 -5
- package/dist/residency/host.js +4 -4
- package/dist/residency/pi-entry.js +4 -4
- package/dist/runtime/core-override-guest-types.js +2 -2
- package/dist/runtime/guest-types.d.ts +1 -1
- package/dist/runtime/guest-types.d.ts.map +1 -1
- package/dist/runtime/guest-types.js +1 -1
- package/dist/runtime/monty-runtime.js +2 -2
- package/dist/runtime/typescript-kernel.js +2 -2
- package/dist/type-error-guidance.js +1 -1
- package/dist/ui/settings-decision-profiles.d.ts +24 -0
- package/dist/ui/settings-decision-profiles.d.ts.map +1 -0
- package/dist/ui/settings-section-context.d.ts +1 -1
- package/dist/ui/settings-section-context.d.ts.map +1 -1
- package/dist/ui/settings-sections-agents.d.ts.map +1 -1
- package/dist/ui/settings-sections-decisions.d.ts +4 -0
- package/dist/ui/settings-sections-decisions.d.ts.map +1 -0
- package/dist/ui/settings-sections.d.ts +1 -1
- package/dist/ui/settings-sections.d.ts.map +1 -1
- package/dist/ui/settings-submenus.d.ts +3 -1
- package/dist/ui/settings-submenus.d.ts.map +1 -1
- package/dist/ui/settings-values.d.ts.map +1 -1
- package/dist/ui/settings.d.ts.map +1 -1
- package/dist/ui/settings.js +215 -41
- package/dist/ui/settings.js.map +4 -4
- package/docs/configuration.md +17 -0
- package/docs/decision-profiles.schema.json +52 -0
- package/docs/decision-test-coverage.md +77 -0
- package/docs/extractive-history.md +17 -2
- package/docs/jev-decisions.md +144 -0
- package/docs/jev.md +5 -0
- package/package.json +2 -2
- package/dist/chunks/chunk-67RXNJLG.js.map +0 -7
- package/dist/chunks/chunk-7OFAO4HK.js.map +0 -7
- package/dist/chunks/chunk-CZPQTISQ.js.map +0 -7
- package/dist/chunks/chunk-IAFI4A2I.js +0 -92
- package/dist/chunks/chunk-IAFI4A2I.js.map +0 -7
- package/dist/chunks/chunk-LQSICKDK.js.map +0 -7
- package/dist/chunks/chunk-QSL5UXNW.js +0 -515
- package/dist/chunks/chunk-QSL5UXNW.js.map +0 -7
- /package/dist/chunks/{chunk-N2VKKA7O.js.map → chunk-4WCEZL6Y.js.map} +0 -0
- /package/dist/chunks/{chunk-K7LQIEJY.js.map → chunk-5DKVWEE4.js.map} +0 -0
- /package/dist/chunks/{chunk-YKT5VLLQ.js.map → chunk-DSNR5U7X.js.map} +0 -0
- /package/dist/chunks/{chunk-R2ZQTTLX.js.map → chunk-EB4DPHWY.js.map} +0 -0
- /package/dist/chunks/{chunk-COQPGIBO.js.map → chunk-HZGQ7XYM.js.map} +0 -0
- /package/dist/chunks/{chunk-OR3FVFRO.js.map → chunk-ZHT5YFY2.js.map} +0 -0
|
@@ -0,0 +1,844 @@
|
|
|
1
|
+
import {
|
|
2
|
+
JevClient,
|
|
3
|
+
JevCredentials
|
|
4
|
+
} from "./chunk-W5CHVJBR.js";
|
|
5
|
+
import {
|
|
6
|
+
checkObserve,
|
|
7
|
+
jevObserveSchema
|
|
8
|
+
} from "./chunk-5DKVWEE4.js";
|
|
9
|
+
import {
|
|
10
|
+
checkSchema,
|
|
11
|
+
checkValue,
|
|
12
|
+
jsonText,
|
|
13
|
+
object
|
|
14
|
+
} from "./chunk-WHBJDTCR.js";
|
|
15
|
+
import {
|
|
16
|
+
validationMessage
|
|
17
|
+
} from "./chunk-A5XF3ZPJ.js";
|
|
18
|
+
import {
|
|
19
|
+
runAbortable
|
|
20
|
+
} from "./chunk-7TB7NXSL.js";
|
|
21
|
+
import {
|
|
22
|
+
ApprovalController,
|
|
23
|
+
FabricAutoApprovalClassifier,
|
|
24
|
+
FabricSessionApprovals
|
|
25
|
+
} from "./chunk-DSNR5U7X.js";
|
|
26
|
+
import {
|
|
27
|
+
DECISION_APIS,
|
|
28
|
+
DECISION_MAX_BYTES
|
|
29
|
+
} from "./chunk-GUZG4GGT.js";
|
|
30
|
+
import {
|
|
31
|
+
resolveJevModelRoute
|
|
32
|
+
} from "./chunk-B54Z662X.js";
|
|
33
|
+
|
|
34
|
+
// src/providers/jev-provider.ts
|
|
35
|
+
import { Type as Type2 } from "typebox";
|
|
36
|
+
|
|
37
|
+
// src/jev/manager.ts
|
|
38
|
+
import { randomUUID } from "node:crypto";
|
|
39
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
40
|
+
|
|
41
|
+
// src/jev/decisions.ts
|
|
42
|
+
var canonical = (provider) => provider === "vercel-ai-gateway" ? "vercel" : provider === "jev" ? "typesafe" : provider;
|
|
43
|
+
var authProvider = (provider) => ["vercel", "vercel-evaluate", "vercel-decisions"].includes(provider) ? "vercel-ai-gateway" : provider;
|
|
44
|
+
var envKeys = {
|
|
45
|
+
typesafe: ["TYPESAFE_API_KEY"],
|
|
46
|
+
openrouter: ["OPENROUTER_API_KEY", "TYPESAFE_OPENROUTER_API_KEY"],
|
|
47
|
+
"vercel-ai-gateway": ["AI_GATEWAY_API_KEY"],
|
|
48
|
+
openai: ["OPENAI_API_KEY"],
|
|
49
|
+
"cloudflare-workers-ai": ["CLOUDFLARE_API_TOKEN", "CLOUDFLARE_API_KEY"],
|
|
50
|
+
opencode: ["OPENCODE_API_KEY"],
|
|
51
|
+
"llama.cpp": ["LLAMA_API_KEY"],
|
|
52
|
+
"llama-system-one": ["LLAMA_API_KEY"],
|
|
53
|
+
anthropic: ["ANTHROPIC_API_KEY"],
|
|
54
|
+
google: ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
|
|
55
|
+
custom: ["DECISION_API_KEY"]
|
|
56
|
+
};
|
|
57
|
+
function decisionEnvironment(config, source = process.env) {
|
|
58
|
+
const env = { ...source };
|
|
59
|
+
for (const key of ["DECISION_PROVIDER", "DECISION_MODEL", "DECISION_ENDPOINT", "DECISION_PROFILE", "DECISION_PROFILES", "DECISION_PROFILES_FILE", "DECISION_CREDENTIAL_COMMAND", ...Object.values(envKeys).flat()]) delete env[key];
|
|
60
|
+
if (config.decisionProfiles) env.DECISION_PROFILES = JSON.stringify(config.decisionProfiles);
|
|
61
|
+
if (config.decisionProfile) env.DECISION_PROFILE = config.decisionProfile;
|
|
62
|
+
return env;
|
|
63
|
+
}
|
|
64
|
+
function registeredTarget(model, presets) {
|
|
65
|
+
const api = { "typesafe-system-one": "system-one", "cloudflare-workers-ai-system-one": "cloudflare", "openai-decisions": "openai-decisions", "llama-cpp-classify": "llama-cpp" }[model.api];
|
|
66
|
+
if (!api || !model.baseUrl) return void 0;
|
|
67
|
+
const provider = canonical(model.provider);
|
|
68
|
+
const preset = presets.find((p) => p.provider === provider);
|
|
69
|
+
const nativeProvider = preset?.api === api ? provider : "custom";
|
|
70
|
+
let endpoint = model.baseUrl.replace(/\/+$/, "");
|
|
71
|
+
const account = process.env.CLOUDFLARE_ACCOUNT_ID;
|
|
72
|
+
if (api === "cloudflare" && account && /^[a-fA-F0-9]{32}$/.test(account)) endpoint = endpoint.replaceAll("{CLOUDFLARE_ACCOUNT_ID}", account);
|
|
73
|
+
if (api === "system-one") endpoint += "/systemone";
|
|
74
|
+
if (api === "openai-decisions") endpoint += "/decisions";
|
|
75
|
+
if (api === "cloudflare") {
|
|
76
|
+
endpoint += "/run";
|
|
77
|
+
if (["clef", "clef-flash", "@cf/cloudflare/clef", "@cf/cloudflare/clef-flash"].includes(model.id)) endpoint += `/@cf/cloudflare/${model.id.split("/").at(-1)}`;
|
|
78
|
+
}
|
|
79
|
+
if (api === "llama-cpp") endpoint = endpoint.replace(/\/v1$/, "");
|
|
80
|
+
return { provider: nativeProvider, api, model: model.id, endpoint };
|
|
81
|
+
}
|
|
82
|
+
function catalog(registry) {
|
|
83
|
+
return registry?.getModelsOfType?.("classifier") ?? [];
|
|
84
|
+
}
|
|
85
|
+
function freeze(value) {
|
|
86
|
+
if (value && typeof value === "object") {
|
|
87
|
+
Object.freeze(value);
|
|
88
|
+
for (const child of Object.values(value)) freeze(child);
|
|
89
|
+
}
|
|
90
|
+
return value;
|
|
91
|
+
}
|
|
92
|
+
function snapshot(value) {
|
|
93
|
+
const text = JSON.stringify(value, (_key, item) => {
|
|
94
|
+
if (typeof item === "number" && !Number.isFinite(item) || ["bigint", "function", "symbol", "undefined"].includes(typeof item)) throw new Error("Decision requests must contain finite JSON values");
|
|
95
|
+
return item;
|
|
96
|
+
});
|
|
97
|
+
if (!text || Buffer.byteLength(text, "utf8") > DECISION_MAX_BYTES) throw new Error("Decision request exceeds 16 MiB JSON limit");
|
|
98
|
+
return freeze(JSON.parse(text));
|
|
99
|
+
}
|
|
100
|
+
function decisionUsage(result) {
|
|
101
|
+
const count = (v) => typeof v === "number" && Number.isSafeInteger(v) && v >= 0 ? v : null;
|
|
102
|
+
const input = count(result?.usage?.input_tokens), output = count(result?.usage?.output_tokens);
|
|
103
|
+
return { input, output, exhausted: input === null || output === null || result?.budget?.exhausted === true };
|
|
104
|
+
}
|
|
105
|
+
var JevDecisions = class _JevDecisions {
|
|
106
|
+
#config;
|
|
107
|
+
#commands = /* @__PURE__ */ new Map();
|
|
108
|
+
#exhausted = /* @__PURE__ */ new WeakSet();
|
|
109
|
+
constructor(config) {
|
|
110
|
+
this.#config = structuredClone(config);
|
|
111
|
+
}
|
|
112
|
+
/** Freeze the selected default for a newly launched managed program. */
|
|
113
|
+
fork() {
|
|
114
|
+
return new _JevDecisions(this.#config);
|
|
115
|
+
}
|
|
116
|
+
/** Trusted UI-only selector update. Existing request snapshots and budgets survive. */
|
|
117
|
+
setProfile(profile) {
|
|
118
|
+
if (profile !== void 0 && profile !== null && (!/^[A-Za-z0-9_.-]{1,128}$/.test(profile) || !this.#config.decisionProfiles || !Object.hasOwn(this.#config.decisionProfiles.profiles, profile)))
|
|
119
|
+
throw new Error("jev.decisionProfile must name a known decision profile");
|
|
120
|
+
if (profile === void 0) delete this.#config.decisionProfile;
|
|
121
|
+
else this.#config.decisionProfile = profile;
|
|
122
|
+
}
|
|
123
|
+
async providers(serve, signal) {
|
|
124
|
+
return serve.request("decisionProviders", {}, signal);
|
|
125
|
+
}
|
|
126
|
+
#profile(request) {
|
|
127
|
+
const profile = request.profile ?? (request.provider === void 0 ? this.#config.decisionProfile ?? this.#config.decisionProfiles?.defaultProfile : void 0);
|
|
128
|
+
if (profile !== void 0) {
|
|
129
|
+
const profiles = this.#config.decisionProfiles?.profiles;
|
|
130
|
+
const target = profiles && Object.hasOwn(profiles, profile) ? profiles[profile] : void 0;
|
|
131
|
+
if (!target) throw new Error("Unknown decision profile");
|
|
132
|
+
if (request.provider !== void 0 && canonical(request.provider) !== canonical(target.provider)) throw new Error("Decision profile and provider disagree");
|
|
133
|
+
}
|
|
134
|
+
return profile;
|
|
135
|
+
}
|
|
136
|
+
async #resolve(serve, original, context) {
|
|
137
|
+
const request = snapshot(original);
|
|
138
|
+
const profile = this.#profile(request);
|
|
139
|
+
const trustedProfile = profile ? this.#config.decisionProfiles.profiles[profile] : void 0;
|
|
140
|
+
const effective = { ...trustedProfile, ...request };
|
|
141
|
+
delete effective.profile;
|
|
142
|
+
const selectedProvider = canonical(effective.provider ?? "typesafe");
|
|
143
|
+
const selectedModel = effective.model;
|
|
144
|
+
const presets = await this.providers(serve, context.signal);
|
|
145
|
+
const models = catalog(context.extensionContext.modelRegistry);
|
|
146
|
+
const registered = models.find((m) => canonical(m.provider) === selectedProvider && m.id === selectedModel);
|
|
147
|
+
const target = registered ? registeredTarget(registered, presets) : void 0;
|
|
148
|
+
const baseline = { ...effective, provider: selectedProvider };
|
|
149
|
+
delete baseline.endpoint;
|
|
150
|
+
if (trustedProfile?.endpoint) {
|
|
151
|
+
baseline.endpoint = trustedProfile.endpoint;
|
|
152
|
+
if (!presets.some((p) => p.provider === selectedProvider)) {
|
|
153
|
+
baseline.provider = "custom";
|
|
154
|
+
if (baseline.api === void 0 && target?.api) baseline.api = target.api;
|
|
155
|
+
}
|
|
156
|
+
} else if (target) {
|
|
157
|
+
if (effective.api !== void 0 && effective.api !== target.api) throw new Error("Decision API differs from the registered classifier target");
|
|
158
|
+
Object.assign(baseline, target);
|
|
159
|
+
}
|
|
160
|
+
if (selectedProvider === "custom" && !trustedProfile && !target) throw new Error("Custom decision targets require a trusted profile or registered classifier");
|
|
161
|
+
const raw = await serve.request("resolveDecision", { request: baseline }, context.signal);
|
|
162
|
+
const resolvedEndpoint = raw.target.endpoint;
|
|
163
|
+
if (request.endpoint !== void 0 && request.endpoint !== resolvedEndpoint) throw new Error("Decision endpoint must exactly match a trusted profile, registered classifier route, or preset default");
|
|
164
|
+
const cleanTarget = {
|
|
165
|
+
provider: raw.target.provider,
|
|
166
|
+
api: raw.target.api,
|
|
167
|
+
model: raw.target.model,
|
|
168
|
+
endpoint: raw.target.endpoint,
|
|
169
|
+
allowLocal: raw.target.allowLocal,
|
|
170
|
+
allowGenerated: raw.target.allowGenerated,
|
|
171
|
+
...raw.target.providerOptions === void 0 ? {} : { providerOptions: raw.target.providerOptions },
|
|
172
|
+
...raw.target.temperature === void 0 ? {} : { temperature: raw.target.temperature }
|
|
173
|
+
};
|
|
174
|
+
const resolution = freeze({ target: cleanTarget, credential: { env: raw.credential.env, resolved: false }, imageSupport: raw.imageSupport, generated: raw.generated, ...profile ? { profile } : {} });
|
|
175
|
+
const prepared = freeze({ ...effective, ...resolution.target });
|
|
176
|
+
return { resolution, request: prepared, auth: authProvider(selectedProvider) };
|
|
177
|
+
}
|
|
178
|
+
async resolve(serve, request, context) {
|
|
179
|
+
return (await this.#resolve(serve, request, context)).resolution;
|
|
180
|
+
}
|
|
181
|
+
async models(serve, context) {
|
|
182
|
+
const presets = await this.providers(serve, context.signal);
|
|
183
|
+
const registry = context.extensionContext.modelRegistry;
|
|
184
|
+
return Promise.all(catalog(registry).map(async (model) => {
|
|
185
|
+
const target = registeredTarget(model, presets);
|
|
186
|
+
let capabilities;
|
|
187
|
+
if (target) {
|
|
188
|
+
try {
|
|
189
|
+
capabilities = await serve.request("resolveDecision", { request: {
|
|
190
|
+
...target,
|
|
191
|
+
...target.api === "llama-cpp" || target.api === "llama-system-one" ? { allowLocal: true } : {},
|
|
192
|
+
state: {},
|
|
193
|
+
questions: { capability: { type: "boolean", instructions: "Offline capability validation only" } }
|
|
194
|
+
} }, context.signal);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (context.signal?.aborted || serve.closed) throw error;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const support = capabilities?.imageSupport ?? "unsupported";
|
|
200
|
+
const provider = authProvider(canonical(model.provider));
|
|
201
|
+
const configured = provider === "typesafe" && registry?.getProviderAuthStatus?.("jev")?.configured || registry?.getProviderAuthStatus?.(provider)?.configured || (envKeys[provider] ?? []).some((key) => !!process.env[key]?.trim()) || provider === authProvider(canonical(resolveJevModelRoute(this.#config.model).route.id)) && this.#config.credentialCommand.length > 0;
|
|
202
|
+
return {
|
|
203
|
+
model: { provider: model.provider, id: model.id },
|
|
204
|
+
name: model.name,
|
|
205
|
+
api: model.api,
|
|
206
|
+
...target ? { target: { ...target, provider: model.provider } } : {},
|
|
207
|
+
supported: !!capabilities,
|
|
208
|
+
imageSupport: !model.input.includes("image") || support === "unsupported" || canonical(model.provider) === "openrouter" ? "unsupported" : support,
|
|
209
|
+
generated: capabilities?.generated ?? false,
|
|
210
|
+
logits: target?.api === "llama-cpp",
|
|
211
|
+
credentials: { configured: !!configured, verified: false }
|
|
212
|
+
};
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
async #credential(provider, registry, signal, local) {
|
|
216
|
+
signal.throwIfAborted();
|
|
217
|
+
try {
|
|
218
|
+
const ids = provider === "typesafe" ? ["jev", "typesafe"] : [provider];
|
|
219
|
+
for (const id of ids) {
|
|
220
|
+
const key = await runAbortable(signal, () => registry?.getApiKeyForProvider?.(id));
|
|
221
|
+
if (key?.trim()) return key.trim();
|
|
222
|
+
}
|
|
223
|
+
} catch {
|
|
224
|
+
throw new Error("Decision Pi credential resolution failed");
|
|
225
|
+
}
|
|
226
|
+
const keys = envKeys[provider] ?? [];
|
|
227
|
+
for (const key of keys) if (process.env[key]?.trim()) return process.env[key].trim();
|
|
228
|
+
const legacyProvider = authProvider(canonical(resolveJevModelRoute(this.#config.model).route.id));
|
|
229
|
+
if (provider === legacyProvider && this.#config.credentialCommand.length) {
|
|
230
|
+
let credentials = this.#commands.get(provider);
|
|
231
|
+
if (!credentials) {
|
|
232
|
+
credentials = new JevCredentials(this.#config.credentialCommand, {}, void 0, keys);
|
|
233
|
+
this.#commands.set(provider, credentials);
|
|
234
|
+
}
|
|
235
|
+
return credentials.resolve(signal);
|
|
236
|
+
}
|
|
237
|
+
if (local) return "";
|
|
238
|
+
throw new Error("Decision credentials unavailable for the selected provider");
|
|
239
|
+
}
|
|
240
|
+
async decide(serve, request, context) {
|
|
241
|
+
if (this.#exhausted.has(serve)) throw new Error("Decision inference budget exhausted or prior usage unknown");
|
|
242
|
+
const signal = AbortSignal.any([...context.signal ? [context.signal] : [], AbortSignal.timeout(this.#config.requestTimeoutMs)]);
|
|
243
|
+
const selected = await this.#resolve(serve, request, { ...context, signal });
|
|
244
|
+
const url = new URL(selected.resolution.target.endpoint);
|
|
245
|
+
const local = selected.resolution.target.allowLocal && ["127.0.0.1", "[::1]"].includes(url.hostname) && !["anthropic", "google"].includes(selected.resolution.target.api);
|
|
246
|
+
const credential = await this.#credential(selected.auth, context.extensionContext.modelRegistry, signal, local);
|
|
247
|
+
signal.throwIfAborted();
|
|
248
|
+
if (this.#exhausted.has(serve)) throw new Error("Decision inference budget exhausted or prior usage unknown");
|
|
249
|
+
try {
|
|
250
|
+
const result = await serve.request("decide", { request: selected.request, timeoutMs: this.#config.requestTimeoutMs, credential }, signal);
|
|
251
|
+
if (decisionUsage(result).exhausted) this.#exhausted.add(serve);
|
|
252
|
+
return result;
|
|
253
|
+
} catch (error) {
|
|
254
|
+
this.#exhausted.add(serve);
|
|
255
|
+
if (signal.aborted) await serve.close();
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
close() {
|
|
260
|
+
for (const credentials of this.#commands.values()) credentials.clear();
|
|
261
|
+
this.#commands.clear();
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
// src/jev/manager.ts
|
|
266
|
+
var prelude = `const input = JSON.parse(\u03C0.__jevInput);
|
|
267
|
+
const program = Object.freeze({
|
|
268
|
+
sleep: (ms: number): Promise<unknown> => tools.call({ ref: "jev.$sleep", args: { ms } }),
|
|
269
|
+
emit: (value: unknown): Promise<unknown> => tools.call({ ref: "jev.$emit", args: { value } }),
|
|
270
|
+
nextEvent: (): Promise<FabricJevHostEvent> => tools.call({ ref: "jev.$nextEvent" }) as Promise<FabricJevHostEvent>,
|
|
271
|
+
advise: (args: {eventId: string; message: string}): Promise<FabricJevAdviceResult> => tools.call({ ref: "jev.advise", args: {...args, id: \u03C0.__jevRunId} }) as Promise<FabricJevAdviceResult>,
|
|
272
|
+
});
|
|
273
|
+
`;
|
|
274
|
+
var JevProgramManager = class {
|
|
275
|
+
constructor(options) {
|
|
276
|
+
this.options = options;
|
|
277
|
+
}
|
|
278
|
+
#runs = /* @__PURE__ */ new Map();
|
|
279
|
+
#approvals = new FabricSessionApprovals();
|
|
280
|
+
#closed = false;
|
|
281
|
+
#starting = 0;
|
|
282
|
+
usesDecisions(id) {
|
|
283
|
+
return this.#runs.get(id)?.usesDecisions ?? false;
|
|
284
|
+
}
|
|
285
|
+
inferenceLimits(id) {
|
|
286
|
+
return this.#runs.get(id)?.inferenceLimits;
|
|
287
|
+
}
|
|
288
|
+
list() {
|
|
289
|
+
return [...this.#runs.values()].map(({ info }) => ({
|
|
290
|
+
id: info.id,
|
|
291
|
+
name: info.name,
|
|
292
|
+
state: info.state,
|
|
293
|
+
background: info.background,
|
|
294
|
+
startedAt: info.startedAt,
|
|
295
|
+
evaluations: info.evaluations,
|
|
296
|
+
toolCalls: info.toolCalls,
|
|
297
|
+
...info.observation ? { observation: structuredClone(info.observation) } : {}
|
|
298
|
+
}));
|
|
299
|
+
}
|
|
300
|
+
#get(id) {
|
|
301
|
+
const run = this.#runs.get(id);
|
|
302
|
+
if (!run) throw new Error("Unknown or expired Jev run ID");
|
|
303
|
+
return run;
|
|
304
|
+
}
|
|
305
|
+
status(id, after = 0) {
|
|
306
|
+
const info = this.#get(id).info;
|
|
307
|
+
return structuredClone({ ...info, events: info.events.filter((e) => e.sequence > after) });
|
|
308
|
+
}
|
|
309
|
+
async wait(id, signal) {
|
|
310
|
+
return structuredClone(await runAbortable(signal, () => this.#get(id).done));
|
|
311
|
+
}
|
|
312
|
+
/** Alias for wait. */
|
|
313
|
+
join(id, signal) {
|
|
314
|
+
return this.wait(id, signal);
|
|
315
|
+
}
|
|
316
|
+
async stop(id) {
|
|
317
|
+
const run = this.#get(id);
|
|
318
|
+
if (run.info.state === "running") run.controller.abort(new Error("Jev run stopped"));
|
|
319
|
+
return structuredClone(await run.done);
|
|
320
|
+
}
|
|
321
|
+
advise(id, eventId, message) {
|
|
322
|
+
if (typeof message !== "string" || !message.trim() || message.length > 2e3 || typeof eventId !== "string" || eventId.length > 256)
|
|
323
|
+
throw new Error("Advice requires a current eventId and 1\u20132000 message characters");
|
|
324
|
+
const run = this.#get(id);
|
|
325
|
+
if (!run.observation) throw new Error("Jev run has no observation subscription");
|
|
326
|
+
return run.observation.advise(eventId, message);
|
|
327
|
+
}
|
|
328
|
+
async launch(request, context, background) {
|
|
329
|
+
const { config, registry } = this.options;
|
|
330
|
+
if (this.#closed) throw new Error("Jev program manager is closed");
|
|
331
|
+
if (this.#starting + [...this.#runs.values()].filter((r) => r.info.state === "running").length >= config.jev.maxConcurrentRuns)
|
|
332
|
+
throw new Error("Jev concurrent run limit reached");
|
|
333
|
+
context.signal?.throwIfAborted();
|
|
334
|
+
this.#starting++;
|
|
335
|
+
let lease;
|
|
336
|
+
let starting = true;
|
|
337
|
+
try {
|
|
338
|
+
const { program: definition, input, observe } = structuredClone(request);
|
|
339
|
+
const observationHost = this.options.observationHost;
|
|
340
|
+
const observationRevision = observationHost?.revision;
|
|
341
|
+
if (observe !== void 0) {
|
|
342
|
+
checkObserve(observe);
|
|
343
|
+
if (!background) throw new Error("Observation requires jev.spawn; foreground runs cannot wait for their own turn to end");
|
|
344
|
+
if (!observationHost) throw new Error("Main lifecycle observation is unavailable in this host");
|
|
345
|
+
if (context.extensionContext.sessionManager?.getSessionId() !== observationHost.sessionId)
|
|
346
|
+
throw new Error("Jev observation is scoped to its owning Main session");
|
|
347
|
+
}
|
|
348
|
+
checkSchema(definition.inputSchema);
|
|
349
|
+
checkSchema(definition.outputSchema);
|
|
350
|
+
checkValue(definition.inputSchema, input, "Program input");
|
|
351
|
+
const requires = [...new Set(definition.requires)];
|
|
352
|
+
for (const ref of requires) {
|
|
353
|
+
if (!/^[a-z][a-z0-9_-]*\.[a-zA-Z0-9_.$-]+$/.test(ref) || ref.startsWith("jev.") && !["jev.evaluate", "jev.decide", "jev.resolveDecision", "jev.decisionProviders", "jev.models", "jev.advise"].includes(ref))
|
|
354
|
+
throw new Error("Programs require exact action refs; recursive Jev lifecycle calls are not allowed");
|
|
355
|
+
if (context.capabilityView && !Object.hasOwn(context.capabilityView.bindings, ref))
|
|
356
|
+
throw new Error(`Jev program cannot widen its caller's capabilities: ${ref}`);
|
|
357
|
+
if (!config.fullCodeMode && (ref.startsWith("pi.") || ref.startsWith("extensions.")))
|
|
358
|
+
throw new Error("Full-code tool access is disabled");
|
|
359
|
+
}
|
|
360
|
+
lease = await registry.acquireCapabilityView(requires, context);
|
|
361
|
+
if (!lease.satisfied || !lease.view) throw new Error(`Missing Jev program capabilities: ${lease.missing.join(", ")}`);
|
|
362
|
+
if (context.capabilityView) for (const ref of requires) {
|
|
363
|
+
const before = context.capabilityView.bindings[ref];
|
|
364
|
+
const after = lease.view.bindings[ref];
|
|
365
|
+
if (before.providerBindingId !== after.providerBindingId || before.descriptorHash !== after.descriptorHash)
|
|
366
|
+
throw new Error("Caller capability generation changed; relaunch with a fresh commitment");
|
|
367
|
+
}
|
|
368
|
+
const { TypeScriptKernelRuntime } = await import("../runtime/typescript-kernel.js");
|
|
369
|
+
const runtime = new TypeScriptKernelRuntime("quickjs");
|
|
370
|
+
const sources = await registry.guestTypeSources({ ...context, capabilityView: lease.view });
|
|
371
|
+
const { code, checked } = runtime.prepare(prelude + definition.code, true, [], sources, [], true);
|
|
372
|
+
if (checked.errors.length) throw new Error(`Jev program typecheck failed: ${checked.errors.map((e) => e.message).join("; ").slice(0, 2e3)}`);
|
|
373
|
+
const approval = new ApprovalController(config.approvals, context.extensionContext, this.#approvals, new FabricAutoApprovalClassifier(() => config.jev));
|
|
374
|
+
if (observe) await approval.approve({
|
|
375
|
+
ref: "jev.spawn",
|
|
376
|
+
provider: "jev",
|
|
377
|
+
name: "spawn",
|
|
378
|
+
description: "Observe future Main lifecycle events and explicitly selected content for a bounded Jev program",
|
|
379
|
+
inputSchema: {},
|
|
380
|
+
risk: "read"
|
|
381
|
+
}, { observe });
|
|
382
|
+
if (this.#closed) throw new Error("Jev program manager is closed");
|
|
383
|
+
context.signal?.throwIfAborted();
|
|
384
|
+
if (observe && observationHost.revision !== observationRevision)
|
|
385
|
+
throw new Error("Main lifecycle changed while preparing the observer; relaunch explicitly with current context");
|
|
386
|
+
const limits = definition.limits ?? {};
|
|
387
|
+
const bounded = (value, fallback, ceiling) => Math.min(value ?? fallback, ceiling);
|
|
388
|
+
const timeoutMs = bounded(limits.timeoutMs, 6e4, config.jev.maxDurationMs);
|
|
389
|
+
const maxEvaluations = bounded(limits.maxEvaluations, 100, config.jev.maxEvaluations);
|
|
390
|
+
const maxToolCalls = bounded(limits.maxToolCalls, 1e3, config.jev.maxToolCalls);
|
|
391
|
+
const maxTokens = bounded(limits.maxTokens, 1e5, config.jev.maxTokens);
|
|
392
|
+
const id = randomUUID();
|
|
393
|
+
const controller = new AbortController();
|
|
394
|
+
const info = {
|
|
395
|
+
id,
|
|
396
|
+
name: definition.name,
|
|
397
|
+
state: "running",
|
|
398
|
+
background,
|
|
399
|
+
startedAt: Date.now(),
|
|
400
|
+
evaluations: 0,
|
|
401
|
+
toolCalls: 0,
|
|
402
|
+
usage: { input_tokens: 0, output_tokens: 0 },
|
|
403
|
+
events: [],
|
|
404
|
+
nextSequence: 1,
|
|
405
|
+
logs: []
|
|
406
|
+
};
|
|
407
|
+
let budgetFailure;
|
|
408
|
+
const failBudget = (message) => {
|
|
409
|
+
budgetFailure = message;
|
|
410
|
+
queueMicrotask(() => controller.abort(new Error(message)));
|
|
411
|
+
throw new Error(message);
|
|
412
|
+
};
|
|
413
|
+
const observation = observe ? observationHost.subscribe(observe, { id, name: definition.name }, controller) : void 0;
|
|
414
|
+
if (observation) info.observation = observation.stats;
|
|
415
|
+
const runLease = lease;
|
|
416
|
+
lease = void 0;
|
|
417
|
+
const abort = () => controller.abort(new Error("Foreground caller cancelled"));
|
|
418
|
+
if (!background) context.signal?.addEventListener("abort", abort, { once: true });
|
|
419
|
+
let timedOut = false;
|
|
420
|
+
const timer = setTimeout(() => {
|
|
421
|
+
timedOut = true;
|
|
422
|
+
controller.abort(new Error("Jev deadline exceeded"));
|
|
423
|
+
}, timeoutMs);
|
|
424
|
+
let evaluating = false;
|
|
425
|
+
let inferenceBlocked = false;
|
|
426
|
+
let reportedTokens = 0;
|
|
427
|
+
const audits = [];
|
|
428
|
+
const emit = (value) => {
|
|
429
|
+
jsonText(value, 4096, "Program event");
|
|
430
|
+
info.events.push({ sequence: info.nextSequence++, at: Date.now(), value });
|
|
431
|
+
if (info.events.length > 64) info.events.shift();
|
|
432
|
+
};
|
|
433
|
+
const run = { info, controller, observation, usesDecisions: requires.includes("jev.decide"), inferenceLimits: { evaluations: maxEvaluations, tokens: maxTokens }, done: void 0 };
|
|
434
|
+
this.#runs.set(id, run);
|
|
435
|
+
this.options.onRunStarted?.(id);
|
|
436
|
+
this.#starting--;
|
|
437
|
+
starting = false;
|
|
438
|
+
this.#prune();
|
|
439
|
+
run.done = (async () => {
|
|
440
|
+
try {
|
|
441
|
+
await delay(0, void 0, { signal: controller.signal });
|
|
442
|
+
const result = await runtime.execute(code, async (rawRef, rawArgs, signal) => {
|
|
443
|
+
signal.throwIfAborted();
|
|
444
|
+
controller.signal.throwIfAborted();
|
|
445
|
+
if (budgetFailure) throw new Error(budgetFailure);
|
|
446
|
+
if (++info.toolCalls > maxToolCalls) return failBudget("Jev tool-call budget exhausted");
|
|
447
|
+
const ref = rawRef === "fabric.$call" ? rawArgs.ref : rawRef;
|
|
448
|
+
const args = rawRef === "fabric.$call" ? rawArgs.args ?? {} : rawArgs;
|
|
449
|
+
if (typeof ref !== "string" || !object(args)) throw new Error("Invalid Jev program tool call");
|
|
450
|
+
if (ref === "jev.$sleep") {
|
|
451
|
+
if (typeof args.ms !== "number" || !Number.isFinite(args.ms) || args.ms < 0 || args.ms > timeoutMs) throw new Error("Invalid program.sleep duration");
|
|
452
|
+
await delay(args.ms, void 0, { signal });
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
if (ref === "jev.$emit") {
|
|
456
|
+
emit(args.value);
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
if (ref === "jev.$nextEvent") {
|
|
460
|
+
if (!observation) throw new Error("program.nextEvent requires observe on jev.spawn");
|
|
461
|
+
return observation.next();
|
|
462
|
+
}
|
|
463
|
+
if (ref === "jev.advise" && args.id !== id) throw new Error("A Jev program can only advise through its own run");
|
|
464
|
+
if (!Object.hasOwn(runLease.view.bindings, ref)) throw new Error(`Capability not granted to Jev program: ${ref}`);
|
|
465
|
+
const inference = ref === "jev.evaluate" || ref === "jev.decide";
|
|
466
|
+
if (inference) {
|
|
467
|
+
if (evaluating) throw new Error("One Jev evaluation may be in flight per program; batch independent questions in one request");
|
|
468
|
+
if (inferenceBlocked || info.evaluations >= maxEvaluations || reportedTokens >= maxTokens)
|
|
469
|
+
return failBudget("Jev inference budget exhausted");
|
|
470
|
+
info.evaluations++;
|
|
471
|
+
evaluating = true;
|
|
472
|
+
}
|
|
473
|
+
let accounted = false;
|
|
474
|
+
try {
|
|
475
|
+
const value = await registry.invoke(ref, args, {
|
|
476
|
+
cwd: context.cwd,
|
|
477
|
+
extensionContext: context.extensionContext,
|
|
478
|
+
signal,
|
|
479
|
+
capabilityView: runLease.view,
|
|
480
|
+
...context.effectPolicy ? { effectPolicy: context.effectPolicy } : {},
|
|
481
|
+
parentToolCallId: `jev:${id}`,
|
|
482
|
+
nestedToolCallId: `jev:${id}:${info.toolCalls}`,
|
|
483
|
+
update() {
|
|
484
|
+
},
|
|
485
|
+
activity() {
|
|
486
|
+
},
|
|
487
|
+
attachMedia() {
|
|
488
|
+
},
|
|
489
|
+
attachPreview() {
|
|
490
|
+
},
|
|
491
|
+
authorize: async (action) => {
|
|
492
|
+
await this.options.authorize?.(action.ref, `jev:${id}`);
|
|
493
|
+
},
|
|
494
|
+
approve: async (action, prepared) => {
|
|
495
|
+
if (action.ref === "schema.commit") {
|
|
496
|
+
await approval.approve({ ...action, risk: "write" }, prepared);
|
|
497
|
+
await approval.approve({ ...action, risk: "execute" }, prepared);
|
|
498
|
+
} else await approval.approve(action, prepared);
|
|
499
|
+
},
|
|
500
|
+
audits,
|
|
501
|
+
maxResultChars: ref === "jev.decide" ? DECISION_MAX_BYTES : ref === "jev.evaluate" ? 1048576 : config.executor.maxNestedResultChars
|
|
502
|
+
});
|
|
503
|
+
if (inference) {
|
|
504
|
+
const response = ref === "jev.decide" ? decisionUsage(value) : {
|
|
505
|
+
input: value.usage.input_tokens,
|
|
506
|
+
output: value.usage.output_tokens,
|
|
507
|
+
exhausted: false
|
|
508
|
+
};
|
|
509
|
+
info.usage.input_tokens = response.input === null || info.usage.input_tokens === null ? null : info.usage.input_tokens + response.input;
|
|
510
|
+
info.usage.output_tokens = response.output === null || info.usage.output_tokens === null ? null : info.usage.output_tokens + response.output;
|
|
511
|
+
reportedTokens += (response.input ?? 0) + (response.output ?? 0);
|
|
512
|
+
inferenceBlocked ||= response.exhausted || reportedTokens >= maxTokens;
|
|
513
|
+
accounted = true;
|
|
514
|
+
if (ref === "jev.evaluate" && reportedTokens > maxTokens)
|
|
515
|
+
return failBudget("Jev reported-token budget exhausted");
|
|
516
|
+
}
|
|
517
|
+
return value;
|
|
518
|
+
} catch (error) {
|
|
519
|
+
if (inference && !accounted) {
|
|
520
|
+
inferenceBlocked = true;
|
|
521
|
+
info.usage.input_tokens = null;
|
|
522
|
+
info.usage.output_tokens = null;
|
|
523
|
+
}
|
|
524
|
+
throw error;
|
|
525
|
+
} finally {
|
|
526
|
+
if (inference) evaluating = false;
|
|
527
|
+
if (audits.length > 64) audits.splice(0, audits.length - 64);
|
|
528
|
+
}
|
|
529
|
+
}, {
|
|
530
|
+
timeoutMs,
|
|
531
|
+
memoryLimitBytes: Math.min(config.executor.memoryLimitBytes, 64 * 1024 * 1024),
|
|
532
|
+
maxCpuSliceMs: 100,
|
|
533
|
+
maxPendingTimers: 128,
|
|
534
|
+
maxLogChars: 4096,
|
|
535
|
+
strings: { __jevInput: JSON.stringify(input), __jevRunId: id },
|
|
536
|
+
signal: controller.signal
|
|
537
|
+
});
|
|
538
|
+
info.logs = result.logs;
|
|
539
|
+
if (budgetFailure) {
|
|
540
|
+
info.state = "failed";
|
|
541
|
+
info.error = budgetFailure;
|
|
542
|
+
} else if (timedOut || result.terminationReason === "timed_out") {
|
|
543
|
+
info.state = "timed_out";
|
|
544
|
+
info.error = result.error ?? "Jev deadline exceeded";
|
|
545
|
+
} else if (controller.signal.aborted || result.terminationReason === "aborted") {
|
|
546
|
+
info.state = "cancelled";
|
|
547
|
+
} else if (result.terminationReason !== "completed") {
|
|
548
|
+
info.state = "failed";
|
|
549
|
+
info.error = result.error ?? "Jev program failed";
|
|
550
|
+
} else {
|
|
551
|
+
checkValue(definition.outputSchema, result.value, "Program output");
|
|
552
|
+
info.result = result.value;
|
|
553
|
+
info.state = "completed";
|
|
554
|
+
}
|
|
555
|
+
} catch (error) {
|
|
556
|
+
info.state = budgetFailure ? "failed" : timedOut ? "timed_out" : controller.signal.aborted ? "cancelled" : "failed";
|
|
557
|
+
info.error = (budgetFailure ?? (error instanceof Error ? error.message : "Jev program failed")).slice(0, 2e3);
|
|
558
|
+
} finally {
|
|
559
|
+
clearTimeout(timer);
|
|
560
|
+
observation?.close();
|
|
561
|
+
context.signal?.removeEventListener("abort", abort);
|
|
562
|
+
await registry.endInvocation(`jev:${id}`).catch(() => void 0);
|
|
563
|
+
await runLease.release();
|
|
564
|
+
info.endedAt = Date.now();
|
|
565
|
+
this.#prune();
|
|
566
|
+
}
|
|
567
|
+
return info;
|
|
568
|
+
})();
|
|
569
|
+
return background ? this.status(id) : structuredClone(await run.done);
|
|
570
|
+
} finally {
|
|
571
|
+
if (starting) this.#starting--;
|
|
572
|
+
await lease?.release();
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
#prune() {
|
|
576
|
+
for (const [id, run] of this.#runs) {
|
|
577
|
+
if (this.#runs.size <= this.options.config.jev.maxRetainedRuns) break;
|
|
578
|
+
if (run.info.state !== "running") this.#runs.delete(id);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
stopAll() {
|
|
582
|
+
for (const run of this.#runs.values()) if (run.info.state === "running") run.controller.abort(new Error("Jev provider closed"));
|
|
583
|
+
}
|
|
584
|
+
async close() {
|
|
585
|
+
this.#closed = true;
|
|
586
|
+
const running = [...this.#runs.values()].filter((r) => r.info.state === "running");
|
|
587
|
+
this.stopAll();
|
|
588
|
+
await Promise.all(running.map((r) => r.done));
|
|
589
|
+
this.#runs.clear();
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
// src/jev/decision-schema.ts
|
|
594
|
+
import { Type } from "typebox";
|
|
595
|
+
var description = Type.Union([Type.String(), Type.Array(Type.Unknown()), Type.Record(Type.String(), Type.Unknown())]);
|
|
596
|
+
var question = Type.Union([
|
|
597
|
+
Type.Object({ type: Type.Union(["boolean", "bool", "noul", "predicate"].map((v) => Type.Literal(v))), instructions: description, criteria: Type.Optional(Type.Object({ true: Type.Optional(description), false: Type.Optional(description) }, { additionalProperties: false })) }, { additionalProperties: false }),
|
|
598
|
+
Type.Object({ type: Type.Literal("choice"), instructions: description, criteria: Type.Record(Type.String(), Type.Union([description, Type.Null()]), { minProperties: 1, maxProperties: 255 }) }, { additionalProperties: false }),
|
|
599
|
+
Type.Object({ type: Type.Literal("score"), instructions: description, criteria: Type.Array(description, { minItems: 2, maxItems: 10 }) }, { additionalProperties: false })
|
|
600
|
+
]);
|
|
601
|
+
var decisionRequestSchema = Type.Object({
|
|
602
|
+
state: description,
|
|
603
|
+
questions: Type.Record(Type.String({ minLength: 1, maxLength: 128 }), question, { minProperties: 1, maxProperties: 128 }),
|
|
604
|
+
profile: Type.Optional(Type.String({ pattern: "^[A-Za-z0-9_.-]{1,128}$" })),
|
|
605
|
+
provider: Type.Optional(Type.String({ minLength: 1, maxLength: 128 })),
|
|
606
|
+
api: Type.Optional(Type.Union(DECISION_APIS.map((v) => Type.Literal(v)))),
|
|
607
|
+
model: Type.Optional(Type.String({ minLength: 1 })),
|
|
608
|
+
endpoint: Type.Optional(Type.String({ minLength: 1 })),
|
|
609
|
+
allowLocal: Type.Optional(Type.Boolean()),
|
|
610
|
+
allowGenerated: Type.Optional(Type.Boolean()),
|
|
611
|
+
providerOptions: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
|
|
612
|
+
temperature: Type.Optional(Type.Number()),
|
|
613
|
+
images: Type.Optional(Type.Array(Type.Object({ type: Type.Optional(Type.Literal("image")), data: Type.String({ minLength: 1 }), mimeType: Type.Union(["image/png", "image/jpeg", "image/webp", "image/gif"].map((v) => Type.Literal(v))) }, { additionalProperties: false }), { maxItems: 128 }))
|
|
614
|
+
}, { additionalProperties: false });
|
|
615
|
+
|
|
616
|
+
// src/providers/jev-provider.ts
|
|
617
|
+
var description2 = Type2.Union([Type2.String(), Type2.Array(Type2.Unknown()), Type2.Record(Type2.String(), Type2.Unknown())]);
|
|
618
|
+
var question2 = Type2.Union([
|
|
619
|
+
Type2.Object({ type: Type2.Literal("noul"), instructions: description2, criteria: Type2.Optional(Type2.Object({ true: Type2.Optional(description2), false: Type2.Optional(description2) }, { additionalProperties: false })) }, { additionalProperties: false }),
|
|
620
|
+
Type2.Object({ type: Type2.Literal("choice"), instructions: description2, criteria: Type2.Record(Type2.String(), Type2.Union([description2, Type2.Null()]), { minProperties: 1, maxProperties: 255 }) }, { additionalProperties: false }),
|
|
621
|
+
Type2.Object({ type: Type2.Literal("score"), instructions: description2, criteria: Type2.Array(description2, { minItems: 2, maxItems: 10 }) }, { additionalProperties: false })
|
|
622
|
+
]);
|
|
623
|
+
var jevRequestSchema = Type2.Object({
|
|
624
|
+
state: description2,
|
|
625
|
+
questions: Type2.Record(Type2.String(), question2, { minProperties: 1, maxProperties: 128 }),
|
|
626
|
+
model: Type2.Optional(Type2.String({ minLength: 1, maxLength: 128 }))
|
|
627
|
+
}, { additionalProperties: false });
|
|
628
|
+
var jevLaunchSchema = Type2.Object({
|
|
629
|
+
program: Type2.Object({
|
|
630
|
+
name: Type2.String({ minLength: 1, maxLength: 128 }),
|
|
631
|
+
code: Type2.String({ minLength: 1, maxLength: 65536 }),
|
|
632
|
+
inputSchema: Type2.Record(Type2.String(), Type2.Unknown()),
|
|
633
|
+
outputSchema: Type2.Record(Type2.String(), Type2.Unknown()),
|
|
634
|
+
requires: Type2.Array(Type2.String({ minLength: 3, maxLength: 256 }), { maxItems: 64, uniqueItems: true }),
|
|
635
|
+
limits: Type2.Optional(Type2.Object({
|
|
636
|
+
timeoutMs: Type2.Optional(Type2.Integer({ minimum: 1 })),
|
|
637
|
+
maxEvaluations: Type2.Optional(Type2.Integer({ minimum: 0 })),
|
|
638
|
+
maxToolCalls: Type2.Optional(Type2.Integer({ minimum: 1 })),
|
|
639
|
+
maxTokens: Type2.Optional(Type2.Integer({ minimum: 1 }))
|
|
640
|
+
}, { additionalProperties: false }))
|
|
641
|
+
}, { additionalProperties: false }),
|
|
642
|
+
input: Type2.Unknown(),
|
|
643
|
+
observe: Type2.Optional(jevObserveSchema)
|
|
644
|
+
}, { additionalProperties: false });
|
|
645
|
+
var idSchema = Type2.Object({ id: Type2.String({ minLength: 1, maxLength: 128 }) }, { additionalProperties: false });
|
|
646
|
+
var statusSchema = Type2.Object({
|
|
647
|
+
id: Type2.Optional(Type2.String({ minLength: 1, maxLength: 128 })),
|
|
648
|
+
after: Type2.Optional(Type2.Integer({ minimum: 0 }))
|
|
649
|
+
}, { additionalProperties: false });
|
|
650
|
+
var jevAdviceSchema = Type2.Object({
|
|
651
|
+
id: Type2.String({ minLength: 1, maxLength: 128 }),
|
|
652
|
+
eventId: Type2.String({ minLength: 1, maxLength: 256 }),
|
|
653
|
+
message: Type2.String({ minLength: 1, maxLength: 2e3 })
|
|
654
|
+
}, { additionalProperties: false });
|
|
655
|
+
var JEV_ACTION_DESCRIPTORS = [
|
|
656
|
+
{ name: "decide", description: "Explicit lossless decision inference through native adapters. Returns status (including error), rawJson, provenance, refusals and nullable usage without legacy normalization. Requires decisions and decision-targets; no classify fallback. Endpoint overrides require an exact trusted profile/registered/preset route. Inspect budget.exhausted; calls consume credits.", inputSchema: decisionRequestSchema, risk: "network", effect: { kind: "emission", resources: ["decision:inference"], ordering: "ordered" } },
|
|
657
|
+
{ name: "resolveDecision", description: "Resolve a per-call decision target offline before credentials. No model requests or key lookup; returns target, unresolved credential env, image support, generated flag and selected profile only.", inputSchema: Type2.Object({ request: decisionRequestSchema }, { additionalProperties: false }), risk: "read" },
|
|
658
|
+
{ name: "decisionProviders", description: "List native decision adapter presets and capabilities offline. These are not verified live models or credential checks.", inputSchema: Type2.Object({}, { additionalProperties: false }), risk: "read" },
|
|
659
|
+
{ name: "models", description: "List registered Pi classifier handles intersected with native decision adapter capabilities, image support, generated/logit provenance and unverified credential presence. No inference or credential resolver execution.", inputSchema: Type2.Object({}, { additionalProperties: false }), risk: "read" },
|
|
660
|
+
{ name: "evaluate", description: "Ask Jev typed Choice, Noul (probability of yes), and Score questions over shared state. No generated text. Batch independent questions. Sends state to TypeSafe and consumes API credits; no automatic retries.", inputSchema: jevRequestSchema, risk: "network", effect: { kind: "emission", resources: ["typesafe:inference"], ordering: "unknown" } },
|
|
661
|
+
{ name: "run", description: "Run a bounded TypeScript shell orchestrator or typed-decision program in the foreground. Returns a terminal run envelope with schema-validated result or error. Globals: input, jev.evaluate, program.sleep(ms), program.emit(value), and Fabric tools restricted to exact requires. Use granted pi.bash and tasks.wait/watch for shell orchestration; maxEvaluations:0 disables program inference. No automatic judgments, host imports or secrets.", inputSchema: jevLaunchSchema, risk: "execute" },
|
|
662
|
+
{ name: "spawn", description: "Launch a session-owned background Jev program. Optional observe requires read approval and subscribes to Main lifecycle events; program.nextEvent waits without polling and shares only explicitly selected bounded context. program.advise needs observe.delivery and requires jev.advise. Use status, wait/join, and stop; not restart-durable.", inputSchema: jevLaunchSchema, risk: "execute" },
|
|
663
|
+
{ name: "status", description: "Without id: credential configuration status (never retrieves secrets) and run summaries. With id: state, usage, bounded events after sequence, logs, result/error. Retains at most 64 events; nextSequence allows detecting gaps.", inputSchema: statusSchema, risk: "read" },
|
|
664
|
+
{ name: "wait", description: "Wait for a background Jev program's terminal run envelope. Cancelling the wait does not cancel the program.", inputSchema: idSchema, risk: "read" },
|
|
665
|
+
{ name: "join", description: "Alias for jev.wait: wait for a background Jev program's terminal run envelope without cancelling the program when the wait is cancelled.", inputSchema: idSchema, risk: "read" },
|
|
666
|
+
{ name: "advise", description: "Deliver bounded advice from an observing Jev run to Main. Requires its current consumed eventId, explicit observe.delivery, freshness, message budget, and the per-input feedback gate. The program helper program.advise supplies its own id. Subject to agent approvals; returns delivered and an optional suppression reason.", inputSchema: jevAdviceSchema, risk: "agent", effect: { kind: "emission", resources: ["main:messages"], ordering: "ordered" } },
|
|
667
|
+
{ name: "stop", description: "Cancel a Jev program and await sandbox cleanup. Idempotent for retained terminal runs; already-issued external effects cannot be undone.", inputSchema: idSchema, risk: "execute" }
|
|
668
|
+
];
|
|
669
|
+
var JevProvider = class {
|
|
670
|
+
name = "jev";
|
|
671
|
+
description = "Shell-first TypeScript orchestration with explicit typed Jev decisions";
|
|
672
|
+
manager;
|
|
673
|
+
client;
|
|
674
|
+
route;
|
|
675
|
+
/** One jev-fabric `serve` connection per program run: one client, one budget, like a Bend program. */
|
|
676
|
+
#runs = /* @__PURE__ */ new Map();
|
|
677
|
+
#transport;
|
|
678
|
+
#jevFabric;
|
|
679
|
+
#decisions;
|
|
680
|
+
#runDecisions = /* @__PURE__ */ new Map();
|
|
681
|
+
#decisionEnv;
|
|
682
|
+
#closed = false;
|
|
683
|
+
constructor(options, client) {
|
|
684
|
+
this.#jevFabric = options.jevFabric;
|
|
685
|
+
this.#decisions = new JevDecisions(options.config.jev);
|
|
686
|
+
this.#decisionEnv = decisionEnvironment(options.config.jev);
|
|
687
|
+
this.manager = new JevProgramManager({ ...options, onRunStarted: (id) => {
|
|
688
|
+
this.#runDecisions.set(`jev:${id}`, this.#decisions.fork());
|
|
689
|
+
options.onRunStarted?.(id);
|
|
690
|
+
} });
|
|
691
|
+
this.route = resolveJevModelRoute(options.config.jev.model).route;
|
|
692
|
+
this.client = client ?? new JevClient(
|
|
693
|
+
options.config.jev,
|
|
694
|
+
void 0,
|
|
695
|
+
new JevCredentials(options.config.jev.credentialCommand, process.env, options.credentialSource, this.route.envKeys),
|
|
696
|
+
this.route
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
/** UI integration: future direct calls/new programs only; never resets the session budget. */
|
|
700
|
+
setDecisionProfile(profile) {
|
|
701
|
+
this.#decisions.setProfile(profile);
|
|
702
|
+
}
|
|
703
|
+
async list(request) {
|
|
704
|
+
const query = request.query?.toLowerCase();
|
|
705
|
+
return JEV_ACTION_DESCRIPTORS.filter((d) => !query || `${d.name} ${d.description}`.toLowerCase().includes(query));
|
|
706
|
+
}
|
|
707
|
+
async describe(name) {
|
|
708
|
+
return JEV_ACTION_DESCRIPTORS.find((d) => d.name === name);
|
|
709
|
+
}
|
|
710
|
+
async invoke(name, args, context) {
|
|
711
|
+
if (this.#closed) throw new Error("Jev provider is closed");
|
|
712
|
+
const descriptor = await this.describe(name);
|
|
713
|
+
if (!descriptor) throw new Error(`Unknown Jev action: ${name}`);
|
|
714
|
+
const invalid = validationMessage(descriptor.inputSchema, args);
|
|
715
|
+
if (invalid) throw new Error(`Invalid jev.${name} arguments: ${invalid}`);
|
|
716
|
+
switch (name) {
|
|
717
|
+
case "decide":
|
|
718
|
+
case "resolveDecision":
|
|
719
|
+
case "decisionProviders":
|
|
720
|
+
case "models": {
|
|
721
|
+
const scope = context.parentToolCallId.startsWith("jev:") ? context.parentToolCallId : "$decision-session";
|
|
722
|
+
const serve = await this.#connection(scope, true);
|
|
723
|
+
const adapter = this.#runDecisions.get(scope) ?? this.#decisions;
|
|
724
|
+
if (name === "decide") return adapter.decide(serve, args, context);
|
|
725
|
+
if (name === "resolveDecision") return adapter.resolve(serve, args.request, context);
|
|
726
|
+
if (name === "models") return adapter.models(serve, context);
|
|
727
|
+
return adapter.providers(serve, context.signal);
|
|
728
|
+
}
|
|
729
|
+
case "evaluate": {
|
|
730
|
+
const dispatch = await this.#dispatchFor(context.parentToolCallId);
|
|
731
|
+
this.#transport = dispatch ? "jev-fabric" : "fabric";
|
|
732
|
+
return this.client.evaluate(args, context.signal ?? new AbortController().signal, dispatch);
|
|
733
|
+
}
|
|
734
|
+
case "run":
|
|
735
|
+
return this.manager.launch(args, context, false);
|
|
736
|
+
case "spawn":
|
|
737
|
+
return this.manager.launch(args, context, true);
|
|
738
|
+
case "status":
|
|
739
|
+
return typeof args.id === "string" ? this.manager.status(args.id, args.after) : {
|
|
740
|
+
credentials: this.client.credentials.status(),
|
|
741
|
+
model: this.client.config.model,
|
|
742
|
+
route: this.route.id,
|
|
743
|
+
runs: this.manager.list(),
|
|
744
|
+
transport: { configured: this.client.config.transport, ...this.#transport ? { last: this.#transport } : {} }
|
|
745
|
+
};
|
|
746
|
+
case "join":
|
|
747
|
+
case "wait":
|
|
748
|
+
return this.manager.wait(args.id, context.signal);
|
|
749
|
+
case "advise":
|
|
750
|
+
return this.manager.advise(args.id, args.eventId, args.message);
|
|
751
|
+
case "stop":
|
|
752
|
+
return this.manager.stop(args.id);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Program decisions go through the run's own jev-fabric connection when
|
|
757
|
+
* configured and available; direct calls stay in-process. `auto` falls back
|
|
758
|
+
* to in-process only when no suitable binary resolves, never after a request fails.
|
|
759
|
+
*/
|
|
760
|
+
async #dispatchFor(scope) {
|
|
761
|
+
if (!scope?.startsWith("jev:")) return void 0;
|
|
762
|
+
const decisions = this.manager.usesDecisions(scope.slice(4));
|
|
763
|
+
if (this.client.config.transport === "fabric" && !decisions) return void 0;
|
|
764
|
+
const serve = await this.#connection(scope, decisions);
|
|
765
|
+
if (!serve) return void 0;
|
|
766
|
+
const provider = this.route.id === "vercel-ai-gateway" ? "vercel" : this.route.id;
|
|
767
|
+
return (request, credential, signal) => serve.request("jev", {
|
|
768
|
+
request,
|
|
769
|
+
provider,
|
|
770
|
+
credential,
|
|
771
|
+
timeoutMs: this.client.config.requestTimeoutMs
|
|
772
|
+
}, signal);
|
|
773
|
+
}
|
|
774
|
+
async #connection(scope, decisions) {
|
|
775
|
+
const transport = this.client.config.transport;
|
|
776
|
+
const bridge = this.#jevFabric;
|
|
777
|
+
if (!bridge) {
|
|
778
|
+
if (decisions || transport === "jev-fabric") throw new Error("Jev decisions/jev.transport jev-fabric needs jev-fabric, which runs on macOS and Linux outside managed hosts");
|
|
779
|
+
return void 0;
|
|
780
|
+
}
|
|
781
|
+
let pending = this.#runs.get(scope);
|
|
782
|
+
if (!pending) {
|
|
783
|
+
pending = (async () => {
|
|
784
|
+
let binary;
|
|
785
|
+
try {
|
|
786
|
+
binary = (await bridge.resolve(decisions ? "decisions" : "jev")).path;
|
|
787
|
+
} catch (error) {
|
|
788
|
+
if (decisions || transport === "jev-fabric") throw error;
|
|
789
|
+
return void 0;
|
|
790
|
+
}
|
|
791
|
+
const { JevFabricServe } = await import("../jev-fabric/serve.js");
|
|
792
|
+
const limits = scope.startsWith("jev:") ? this.manager.inferenceLimits(scope.slice(4)) : void 0;
|
|
793
|
+
return JevFabricServe.open(binary, {
|
|
794
|
+
home: bridge.home,
|
|
795
|
+
cwd: bridge.options.cwd,
|
|
796
|
+
timeoutMs: 864e5,
|
|
797
|
+
evaluations: limits?.evaluations ?? this.client.config.maxEvaluations,
|
|
798
|
+
tokens: limits?.tokens ?? this.client.config.maxTokens,
|
|
799
|
+
env: this.#decisionEnv
|
|
800
|
+
});
|
|
801
|
+
})();
|
|
802
|
+
this.#runs.set(scope, pending);
|
|
803
|
+
}
|
|
804
|
+
const serve = await pending;
|
|
805
|
+
if (decisions && (!serve || serve.banner.protocol < 2 || !["decisions", "decision-targets"].every((feature) => serve.banner.features?.includes(feature)))) {
|
|
806
|
+
await serve?.close();
|
|
807
|
+
throw new Error("Lossless decisions require native decisions and decision-targets features; no legacy fallback is permitted");
|
|
808
|
+
}
|
|
809
|
+
return serve;
|
|
810
|
+
}
|
|
811
|
+
async invocationEnded(parentToolCallId) {
|
|
812
|
+
this.#runDecisions.get(parentToolCallId)?.close();
|
|
813
|
+
this.#runDecisions.delete(parentToolCallId);
|
|
814
|
+
const pending = this.#runs.get(parentToolCallId);
|
|
815
|
+
if (!pending) return;
|
|
816
|
+
this.#runs.delete(parentToolCallId);
|
|
817
|
+
await (await pending.catch(() => void 0))?.close();
|
|
818
|
+
}
|
|
819
|
+
async close() {
|
|
820
|
+
this.#closed = true;
|
|
821
|
+
await this.manager.close();
|
|
822
|
+
this.client.close();
|
|
823
|
+
this.#decisions.close();
|
|
824
|
+
for (const adapter of this.#runDecisions.values()) adapter.close();
|
|
825
|
+
this.#runDecisions.clear();
|
|
826
|
+
const connections = [...this.#runs.values()];
|
|
827
|
+
this.#runs.clear();
|
|
828
|
+
await Promise.allSettled(connections.map(async (pending) => (await pending)?.close()));
|
|
829
|
+
}
|
|
830
|
+
};
|
|
831
|
+
|
|
832
|
+
export {
|
|
833
|
+
decisionEnvironment,
|
|
834
|
+
decisionUsage,
|
|
835
|
+
JevDecisions,
|
|
836
|
+
JevProgramManager,
|
|
837
|
+
decisionRequestSchema,
|
|
838
|
+
jevRequestSchema,
|
|
839
|
+
jevLaunchSchema,
|
|
840
|
+
jevAdviceSchema,
|
|
841
|
+
JEV_ACTION_DESCRIPTORS,
|
|
842
|
+
JevProvider
|
|
843
|
+
};
|
|
844
|
+
//# sourceMappingURL=chunk-3R6NIF2N.js.map
|