@granular-software/sdk 0.4.49 → 0.4.51
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 +53 -0
- package/dist/agent-evals.d.mts +2 -2
- package/dist/agent-evals.d.ts +2 -2
- package/dist/agent-evals.js +1551 -277
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +1551 -277
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/agent-harness.d.mts +19 -2
- package/dist/agent-harness.d.ts +19 -2
- package/dist/agent-harness.js +81 -26
- package/dist/agent-harness.js.map +1 -1
- package/dist/agent-harness.mjs +80 -27
- package/dist/agent-harness.mjs.map +1 -1
- package/dist/cli/index.js +1492 -266
- package/dist/{client-B-MPVvDr.d.mts → client-NSH-tpNU.d.ts} +88 -11
- package/dist/{client-zihxkDDs.d.ts → client-djorlOpn.d.mts} +88 -11
- package/dist/index.d.mts +104 -5
- package/dist/index.d.ts +104 -5
- package/dist/index.js +1633 -268
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1630 -269
- package/dist/index.mjs.map +1 -1
- package/dist/{spend-CStuOBXb.d.mts → spend-BA-jZwZ0.d.mts} +505 -12
- package/dist/{spend-CStuOBXb.d.ts → spend-BA-jZwZ0.d.ts} +505 -12
- package/dist/spend.d.mts +1 -1
- package/dist/spend.d.ts +1 -1
- package/dist/spend.js +6 -1
- package/dist/spend.js.map +1 -1
- package/dist/spend.mjs +6 -1
- package/dist/spend.mjs.map +1 -1
- package/package.json +2 -1
package/dist/agent-evals.mjs
CHANGED
|
@@ -25,11 +25,11 @@ var __export = (target, all) => {
|
|
|
25
25
|
for (var name in all)
|
|
26
26
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
27
27
|
};
|
|
28
|
-
var __copyProps = (to,
|
|
29
|
-
if (
|
|
30
|
-
for (let key of __getOwnPropNames(
|
|
28
|
+
var __copyProps = (to, from2, except, desc) => {
|
|
29
|
+
if (from2 && typeof from2 === "object" || typeof from2 === "function") {
|
|
30
|
+
for (let key of __getOwnPropNames(from2))
|
|
31
31
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
32
|
-
__defProp(to, key, { get: () =>
|
|
32
|
+
__defProp(to, key, { get: () => from2[key], enumerable: !(desc = __getOwnPropDesc(from2, key)) || desc.enumerable });
|
|
33
33
|
}
|
|
34
34
|
return to;
|
|
35
35
|
};
|
|
@@ -4019,6 +4019,9 @@ function rpcTimeoutMsForMethod(method) {
|
|
|
4019
4019
|
return DOMAIN_PACKAGE_RPC_TIMEOUT_MS;
|
|
4020
4020
|
case "client.heartbeat":
|
|
4021
4021
|
case "effects.publishCatalog":
|
|
4022
|
+
case "effects.resetCatalog":
|
|
4023
|
+
case "effects.addCatalog":
|
|
4024
|
+
case "effects.removeCatalog":
|
|
4022
4025
|
case "effects.refresh":
|
|
4023
4026
|
return EFFECT_CONTROL_RPC_TIMEOUT_MS;
|
|
4024
4027
|
case "harness.run":
|
|
@@ -4043,16 +4046,37 @@ var WSClient = class {
|
|
|
4043
4046
|
tokenRefreshTimer = null;
|
|
4044
4047
|
isExplicitlyDisconnected = false;
|
|
4045
4048
|
reconnectAttempts = 0;
|
|
4049
|
+
connectPromise = null;
|
|
4050
|
+
connectionEpoch = 0;
|
|
4051
|
+
cancelConnectAttempt = null;
|
|
4046
4052
|
options;
|
|
4047
4053
|
constructor(options) {
|
|
4048
4054
|
this.options = options;
|
|
4049
4055
|
this.url = options.url;
|
|
4050
4056
|
this.sessionId = options.sessionId;
|
|
4051
4057
|
this.token = options.token;
|
|
4058
|
+
if (options.initialDocumentSnapshot) {
|
|
4059
|
+
this.seedDocumentSnapshot(options.initialDocumentSnapshot);
|
|
4060
|
+
}
|
|
4052
4061
|
}
|
|
4053
4062
|
get currentSessionId() {
|
|
4054
4063
|
return this.sessionId;
|
|
4055
4064
|
}
|
|
4065
|
+
seedDocumentSnapshot(document) {
|
|
4066
|
+
if (!document || typeof document !== "object" || Array.isArray(document)) {
|
|
4067
|
+
return;
|
|
4068
|
+
}
|
|
4069
|
+
try {
|
|
4070
|
+
this.doc = document instanceof Uint8Array ? Automerge.load(document) : Automerge.from(document);
|
|
4071
|
+
this.syncState = Automerge.initSyncState();
|
|
4072
|
+
this.emit("sync", this.doc);
|
|
4073
|
+
} catch (error) {
|
|
4074
|
+
console.warn("[Granular] Failed to seed cached session document", error);
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
saveDocumentSnapshot() {
|
|
4078
|
+
return Automerge.save(this.doc);
|
|
4079
|
+
}
|
|
4056
4080
|
clearTokenRefreshTimer() {
|
|
4057
4081
|
if (this.tokenRefreshTimer) {
|
|
4058
4082
|
clearTimeout(this.tokenRefreshTimer);
|
|
@@ -4162,8 +4186,23 @@ var WSClient = class {
|
|
|
4162
4186
|
* Connect to the WebSocket server
|
|
4163
4187
|
* @returns {Promise<void>} Resolves when connection is open
|
|
4164
4188
|
*/
|
|
4165
|
-
async connect() {
|
|
4189
|
+
async connect(options = {}) {
|
|
4190
|
+
if (this.ws?.readyState === READY_STATE_OPEN) return;
|
|
4191
|
+
if (this.connectPromise) return this.connectPromise;
|
|
4192
|
+
const connectPromise = this.connectAttempt(options.signal);
|
|
4193
|
+
this.connectPromise = connectPromise;
|
|
4194
|
+
try {
|
|
4195
|
+
await connectPromise;
|
|
4196
|
+
} finally {
|
|
4197
|
+
if (this.connectPromise === connectPromise) {
|
|
4198
|
+
this.connectPromise = null;
|
|
4199
|
+
}
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
async connectAttempt(signal) {
|
|
4203
|
+
if (signal?.aborted) throw new Error("WebSocket connect aborted");
|
|
4166
4204
|
const token = await this.resolveTokenForConnect();
|
|
4205
|
+
if (signal?.aborted) throw new Error("WebSocket connect aborted");
|
|
4167
4206
|
this.isExplicitlyDisconnected = false;
|
|
4168
4207
|
this.scheduleTokenRefresh();
|
|
4169
4208
|
if (this.reconnectTimer) {
|
|
@@ -4175,7 +4214,7 @@ var WSClient = class {
|
|
|
4175
4214
|
try {
|
|
4176
4215
|
const wsModule = await Promise.resolve().then(() => (init_wrapper(), wrapper_exports));
|
|
4177
4216
|
WebSocketClass = wsModule.default || wsModule;
|
|
4178
|
-
} catch
|
|
4217
|
+
} catch {
|
|
4179
4218
|
}
|
|
4180
4219
|
}
|
|
4181
4220
|
if (!WebSocketClass) {
|
|
@@ -4183,83 +4222,97 @@ var WSClient = class {
|
|
|
4183
4222
|
'No WebSocket implementation found. If using Node.js, please install "ws" and pass the constructor to the SDK options: { WebSocketCtor: WebSocket }.'
|
|
4184
4223
|
);
|
|
4185
4224
|
}
|
|
4225
|
+
const epoch = ++this.connectionEpoch;
|
|
4226
|
+
const wsUrl = new URL(this.url);
|
|
4227
|
+
wsUrl.searchParams.set("sessionId", this.sessionId);
|
|
4228
|
+
wsUrl.searchParams.set("token", token);
|
|
4229
|
+
const socket = new WebSocketClass(wsUrl.toString());
|
|
4230
|
+
this.ws = socket;
|
|
4186
4231
|
return new Promise((resolve, reject) => {
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
if (
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
this.reconnectTimer = null;
|
|
4199
|
-
}
|
|
4200
|
-
this.reconnectAttempts = 0;
|
|
4201
|
-
this.emit("open", {});
|
|
4202
|
-
resolve();
|
|
4203
|
-
});
|
|
4204
|
-
socket.on("message", (data) => {
|
|
4205
|
-
try {
|
|
4206
|
-
const message = JSON.parse(data.toString());
|
|
4207
|
-
this.handleMessage(message);
|
|
4208
|
-
} catch (error) {
|
|
4209
|
-
console.error("[Granular] Failed to parse message:", error);
|
|
4210
|
-
}
|
|
4211
|
-
});
|
|
4212
|
-
socket.on("error", (error) => {
|
|
4213
|
-
this.emit("error", error);
|
|
4214
|
-
if (socket.readyState !== READY_STATE_OPEN) {
|
|
4215
|
-
reject(error);
|
|
4216
|
-
}
|
|
4217
|
-
});
|
|
4218
|
-
socket.on("close", (code, reason) => {
|
|
4219
|
-
this.handleDisconnect({
|
|
4220
|
-
code,
|
|
4221
|
-
reason: this.normalizeReason(reason),
|
|
4222
|
-
// ws does not provide wasClean on Node-style close callback
|
|
4223
|
-
wasClean: code === 1e3
|
|
4224
|
-
});
|
|
4225
|
-
});
|
|
4232
|
+
let settled = false;
|
|
4233
|
+
const isCurrent = () => this.connectionEpoch === epoch && this.ws === socket;
|
|
4234
|
+
const finish = (error) => {
|
|
4235
|
+
if (settled) return;
|
|
4236
|
+
settled = true;
|
|
4237
|
+
if (this.cancelConnectAttempt === handleAbort) {
|
|
4238
|
+
this.cancelConnectAttempt = null;
|
|
4239
|
+
}
|
|
4240
|
+
signal?.removeEventListener("abort", handleAbort);
|
|
4241
|
+
if (error) {
|
|
4242
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
4226
4243
|
} else {
|
|
4227
|
-
|
|
4228
|
-
if (this.reconnectTimer) {
|
|
4229
|
-
clearTimeout(this.reconnectTimer);
|
|
4230
|
-
this.reconnectTimer = null;
|
|
4231
|
-
}
|
|
4232
|
-
this.reconnectAttempts = 0;
|
|
4233
|
-
this.emit("open", {});
|
|
4234
|
-
resolve();
|
|
4235
|
-
};
|
|
4236
|
-
this.ws.onmessage = (event) => {
|
|
4237
|
-
try {
|
|
4238
|
-
const data = event.data;
|
|
4239
|
-
const message = JSON.parse(data.toString());
|
|
4240
|
-
this.handleMessage(message);
|
|
4241
|
-
} catch (error) {
|
|
4242
|
-
console.error("[Granular] Failed to parse message:", error);
|
|
4243
|
-
}
|
|
4244
|
-
};
|
|
4245
|
-
this.ws.onerror = (event) => {
|
|
4246
|
-
const error = new Error("WebSocket error");
|
|
4247
|
-
error.event = event;
|
|
4248
|
-
this.emit("error", error);
|
|
4249
|
-
if (this.ws?.readyState !== READY_STATE_OPEN) {
|
|
4250
|
-
reject(error);
|
|
4251
|
-
}
|
|
4252
|
-
};
|
|
4253
|
-
this.ws.onclose = (event) => {
|
|
4254
|
-
this.handleDisconnect({
|
|
4255
|
-
code: event.code,
|
|
4256
|
-
reason: event.reason,
|
|
4257
|
-
wasClean: event.wasClean
|
|
4258
|
-
});
|
|
4259
|
-
};
|
|
4244
|
+
resolve();
|
|
4260
4245
|
}
|
|
4261
|
-
}
|
|
4262
|
-
|
|
4246
|
+
};
|
|
4247
|
+
const closeStaleSocket = () => {
|
|
4248
|
+
try {
|
|
4249
|
+
socket.close(1e3, "Stale connection attempt");
|
|
4250
|
+
} catch {
|
|
4251
|
+
}
|
|
4252
|
+
};
|
|
4253
|
+
const handleAbort = () => {
|
|
4254
|
+
if (isCurrent()) {
|
|
4255
|
+
this.connectionEpoch += 1;
|
|
4256
|
+
this.ws = null;
|
|
4257
|
+
}
|
|
4258
|
+
closeStaleSocket();
|
|
4259
|
+
finish(new Error("WebSocket connect aborted"));
|
|
4260
|
+
};
|
|
4261
|
+
this.cancelConnectAttempt = handleAbort;
|
|
4262
|
+
const handleOpen = () => {
|
|
4263
|
+
if (!isCurrent()) {
|
|
4264
|
+
closeStaleSocket();
|
|
4265
|
+
return;
|
|
4266
|
+
}
|
|
4267
|
+
this.reconnectAttempts = 0;
|
|
4268
|
+
this.emit("open", {});
|
|
4269
|
+
finish();
|
|
4270
|
+
};
|
|
4271
|
+
const handleMessage = (data) => {
|
|
4272
|
+
if (!isCurrent()) return;
|
|
4273
|
+
try {
|
|
4274
|
+
const text = typeof data === "string" ? data : data && typeof data === "object" && "toString" in data ? String(data.toString()) : "";
|
|
4275
|
+
this.handleMessage(JSON.parse(text));
|
|
4276
|
+
} catch (error) {
|
|
4277
|
+
console.error("[Granular] Failed to parse message:", error);
|
|
4278
|
+
}
|
|
4279
|
+
};
|
|
4280
|
+
const handleError = (error) => {
|
|
4281
|
+
if (!isCurrent()) return;
|
|
4282
|
+
const typedError = error instanceof Error ? error : new Error("WebSocket error");
|
|
4283
|
+
this.emit("error", typedError);
|
|
4284
|
+
if (socket.readyState !== READY_STATE_OPEN) finish(typedError);
|
|
4285
|
+
};
|
|
4286
|
+
const handleClose = (close) => {
|
|
4287
|
+
if (!isCurrent()) return;
|
|
4288
|
+
if (!settled) {
|
|
4289
|
+
finish(
|
|
4290
|
+
new Error(
|
|
4291
|
+
`WebSocket closed before ready${close.code ? ` (code=${close.code})` : ""}`
|
|
4292
|
+
)
|
|
4293
|
+
);
|
|
4294
|
+
}
|
|
4295
|
+
this.handleDisconnect({
|
|
4296
|
+
code: close.code,
|
|
4297
|
+
reason: this.normalizeReason(close.reason),
|
|
4298
|
+
wasClean: close.wasClean
|
|
4299
|
+
});
|
|
4300
|
+
};
|
|
4301
|
+
signal?.addEventListener("abort", handleAbort, { once: true });
|
|
4302
|
+
const nodeSocket = socket;
|
|
4303
|
+
if (typeof nodeSocket.on === "function") {
|
|
4304
|
+
nodeSocket.on("open", handleOpen);
|
|
4305
|
+
nodeSocket.on("message", handleMessage);
|
|
4306
|
+
nodeSocket.on("error", handleError);
|
|
4307
|
+
nodeSocket.on(
|
|
4308
|
+
"close",
|
|
4309
|
+
(code, reason) => handleClose({ code, reason, wasClean: code === 1e3 })
|
|
4310
|
+
);
|
|
4311
|
+
} else {
|
|
4312
|
+
socket.onopen = handleOpen;
|
|
4313
|
+
socket.onmessage = (event) => handleMessage(event.data);
|
|
4314
|
+
socket.onerror = handleError;
|
|
4315
|
+
socket.onclose = (event) => handleClose(event);
|
|
4263
4316
|
}
|
|
4264
4317
|
});
|
|
4265
4318
|
}
|
|
@@ -4277,9 +4330,58 @@ var WSClient = class {
|
|
|
4277
4330
|
return void 0;
|
|
4278
4331
|
}
|
|
4279
4332
|
rejectPending(error) {
|
|
4280
|
-
this.messageQueue.forEach((pending) =>
|
|
4333
|
+
this.messageQueue.forEach((pending) => {
|
|
4334
|
+
clearTimeout(pending.timeout);
|
|
4335
|
+
pending.reject(error);
|
|
4336
|
+
});
|
|
4281
4337
|
this.messageQueue = [];
|
|
4282
4338
|
}
|
|
4339
|
+
emitReconnectErrorMessage(error) {
|
|
4340
|
+
const reconnectInfo = {
|
|
4341
|
+
error,
|
|
4342
|
+
sessionId: this.sessionId,
|
|
4343
|
+
timestamp: Date.now()
|
|
4344
|
+
};
|
|
4345
|
+
this.emit("reconnect_error", reconnectInfo);
|
|
4346
|
+
if (this.options.onReconnectError) {
|
|
4347
|
+
try {
|
|
4348
|
+
this.options.onReconnectError(reconnectInfo);
|
|
4349
|
+
} catch (callbackError) {
|
|
4350
|
+
console.error(
|
|
4351
|
+
"[Granular] onReconnectError callback failed:",
|
|
4352
|
+
callbackError
|
|
4353
|
+
);
|
|
4354
|
+
}
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
scheduleReconnectAttempt() {
|
|
4358
|
+
if (this.isExplicitlyDisconnected || this.reconnectTimer) return null;
|
|
4359
|
+
const baseReconnectDelayMs = typeof this.options.reconnectDelayMs === "number" && Number.isFinite(this.options.reconnectDelayMs) && this.options.reconnectDelayMs > 0 ? this.options.reconnectDelayMs : DEFAULT_RECONNECT_DELAY_MS;
|
|
4360
|
+
const maxReconnectAttempts = typeof this.options.maxReconnectAttempts === "number" && Number.isFinite(this.options.maxReconnectAttempts) && this.options.maxReconnectAttempts >= 0 ? Math.floor(this.options.maxReconnectAttempts) : DEFAULT_MAX_RECONNECT_ATTEMPTS;
|
|
4361
|
+
if (this.reconnectAttempts >= maxReconnectAttempts) {
|
|
4362
|
+
this.emitReconnectErrorMessage(
|
|
4363
|
+
`WebSocket reconnect attempts exhausted after ${maxReconnectAttempts} attempt(s).`
|
|
4364
|
+
);
|
|
4365
|
+
return null;
|
|
4366
|
+
}
|
|
4367
|
+
this.reconnectAttempts += 1;
|
|
4368
|
+
const reconnectDelayMs = Math.min(
|
|
4369
|
+
3e4,
|
|
4370
|
+
baseReconnectDelayMs * 2 ** Math.max(0, this.reconnectAttempts - 1)
|
|
4371
|
+
);
|
|
4372
|
+
this.reconnectTimer = setTimeout(() => {
|
|
4373
|
+
this.reconnectTimer = null;
|
|
4374
|
+
console.log("[Granular] Attempting reconnect...");
|
|
4375
|
+
this.connect().catch((error) => {
|
|
4376
|
+
console.error("[Granular] Reconnect failed:", error);
|
|
4377
|
+
this.emitReconnectErrorMessage(
|
|
4378
|
+
error instanceof Error ? error.message : String(error)
|
|
4379
|
+
);
|
|
4380
|
+
this.scheduleReconnectAttempt();
|
|
4381
|
+
});
|
|
4382
|
+
}, reconnectDelayMs);
|
|
4383
|
+
return reconnectDelayMs;
|
|
4384
|
+
}
|
|
4283
4385
|
buildDisconnectError(info) {
|
|
4284
4386
|
const details = [
|
|
4285
4387
|
info.code !== void 0 ? `code=${info.code}` : void 0,
|
|
@@ -4289,8 +4391,6 @@ var WSClient = class {
|
|
|
4289
4391
|
return new Error(`WebSocket disconnected${suffix}`);
|
|
4290
4392
|
}
|
|
4291
4393
|
handleDisconnect(close = {}) {
|
|
4292
|
-
const baseReconnectDelayMs = typeof this.options.reconnectDelayMs === "number" && Number.isFinite(this.options.reconnectDelayMs) && this.options.reconnectDelayMs > 0 ? this.options.reconnectDelayMs : DEFAULT_RECONNECT_DELAY_MS;
|
|
4293
|
-
const maxReconnectAttempts = typeof this.options.maxReconnectAttempts === "number" && Number.isFinite(this.options.maxReconnectAttempts) && this.options.maxReconnectAttempts >= 0 ? Math.floor(this.options.maxReconnectAttempts) : DEFAULT_MAX_RECONNECT_ATTEMPTS;
|
|
4294
4394
|
const unexpected = !this.isExplicitlyDisconnected;
|
|
4295
4395
|
const info = {
|
|
4296
4396
|
code: close.code,
|
|
@@ -4310,32 +4410,9 @@ var WSClient = class {
|
|
|
4310
4410
|
const disconnectError = this.buildDisconnectError(info);
|
|
4311
4411
|
this.rejectPending(disconnectError);
|
|
4312
4412
|
this.emit("disconnect", info);
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
sessionId: this.sessionId,
|
|
4317
|
-
timestamp: Date.now()
|
|
4318
|
-
};
|
|
4319
|
-
this.emit("reconnect_error", reconnectInfo);
|
|
4320
|
-
if (this.options.onReconnectError) {
|
|
4321
|
-
try {
|
|
4322
|
-
this.options.onReconnectError(reconnectInfo);
|
|
4323
|
-
} catch (callbackError) {
|
|
4324
|
-
console.error(
|
|
4325
|
-
"[Granular] onReconnectError callback failed:",
|
|
4326
|
-
callbackError
|
|
4327
|
-
);
|
|
4328
|
-
}
|
|
4329
|
-
}
|
|
4330
|
-
return;
|
|
4331
|
-
}
|
|
4332
|
-
this.reconnectAttempts += 1;
|
|
4333
|
-
const reconnectDelayMs = Math.min(
|
|
4334
|
-
3e4,
|
|
4335
|
-
baseReconnectDelayMs * 2 ** Math.max(0, this.reconnectAttempts - 1)
|
|
4336
|
-
);
|
|
4337
|
-
info.reconnectScheduled = true;
|
|
4338
|
-
info.reconnectDelayMs = reconnectDelayMs;
|
|
4413
|
+
const reconnectDelayMs = this.scheduleReconnectAttempt();
|
|
4414
|
+
info.reconnectScheduled = reconnectDelayMs !== null;
|
|
4415
|
+
if (reconnectDelayMs !== null) info.reconnectDelayMs = reconnectDelayMs;
|
|
4339
4416
|
if (this.options.onUnexpectedClose) {
|
|
4340
4417
|
try {
|
|
4341
4418
|
this.options.onUnexpectedClose(info);
|
|
@@ -4346,28 +4423,6 @@ var WSClient = class {
|
|
|
4346
4423
|
);
|
|
4347
4424
|
}
|
|
4348
4425
|
}
|
|
4349
|
-
this.reconnectTimer = setTimeout(() => {
|
|
4350
|
-
console.log("[Granular] Attempting reconnect...");
|
|
4351
|
-
this.connect().catch((error) => {
|
|
4352
|
-
console.error("[Granular] Reconnect failed:", error);
|
|
4353
|
-
const reconnectInfo = {
|
|
4354
|
-
error: error instanceof Error ? error.message : String(error),
|
|
4355
|
-
sessionId: this.sessionId,
|
|
4356
|
-
timestamp: Date.now()
|
|
4357
|
-
};
|
|
4358
|
-
this.emit("reconnect_error", reconnectInfo);
|
|
4359
|
-
if (this.options.onReconnectError) {
|
|
4360
|
-
try {
|
|
4361
|
-
this.options.onReconnectError(reconnectInfo);
|
|
4362
|
-
} catch (callbackError) {
|
|
4363
|
-
console.error(
|
|
4364
|
-
"[Granular] onReconnectError callback failed:",
|
|
4365
|
-
callbackError
|
|
4366
|
-
);
|
|
4367
|
-
}
|
|
4368
|
-
}
|
|
4369
|
-
});
|
|
4370
|
-
}, reconnectDelayMs);
|
|
4371
4426
|
}
|
|
4372
4427
|
}
|
|
4373
4428
|
handleMessage(message) {
|
|
@@ -4478,6 +4533,7 @@ var WSClient = class {
|
|
|
4478
4533
|
const response = message;
|
|
4479
4534
|
const pending = this.messageQueue.find((q) => q.id === response.id);
|
|
4480
4535
|
if (pending) {
|
|
4536
|
+
clearTimeout(pending.timeout);
|
|
4481
4537
|
if (response.type === "rpc_error") {
|
|
4482
4538
|
pending.reject(
|
|
4483
4539
|
new Error(
|
|
@@ -4523,16 +4579,22 @@ var WSClient = class {
|
|
|
4523
4579
|
id
|
|
4524
4580
|
};
|
|
4525
4581
|
return new Promise((resolve, reject) => {
|
|
4526
|
-
this.messageQueue.push({ resolve, reject, id });
|
|
4527
|
-
this.ws.send(JSON.stringify(request));
|
|
4528
4582
|
const timeoutMs = rpcTimeoutMsForMethod(method);
|
|
4529
|
-
setTimeout(() => {
|
|
4583
|
+
const timeout = setTimeout(() => {
|
|
4530
4584
|
const pending = this.messageQueue.find((q) => q.id === id);
|
|
4531
4585
|
if (pending) {
|
|
4532
4586
|
this.messageQueue = this.messageQueue.filter((q) => q.id !== id);
|
|
4533
4587
|
reject(new Error(`RPC timeout: ${method}`));
|
|
4534
4588
|
}
|
|
4535
4589
|
}, timeoutMs);
|
|
4590
|
+
this.messageQueue.push({ resolve, reject, id, timeout });
|
|
4591
|
+
try {
|
|
4592
|
+
this.ws.send(JSON.stringify(request));
|
|
4593
|
+
} catch (error) {
|
|
4594
|
+
clearTimeout(timeout);
|
|
4595
|
+
this.messageQueue = this.messageQueue.filter((q) => q.id !== id);
|
|
4596
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
4597
|
+
}
|
|
4536
4598
|
});
|
|
4537
4599
|
}
|
|
4538
4600
|
async handleIncomingRpc(request) {
|
|
@@ -4618,15 +4680,18 @@ var WSClient = class {
|
|
|
4618
4680
|
/**
|
|
4619
4681
|
* Disconnect the WebSocket and clear state
|
|
4620
4682
|
*/
|
|
4621
|
-
disconnect() {
|
|
4683
|
+
disconnect(options = {}) {
|
|
4622
4684
|
this.isExplicitlyDisconnected = true;
|
|
4685
|
+
this.cancelConnectAttempt?.();
|
|
4686
|
+
this.cancelConnectAttempt = null;
|
|
4687
|
+
this.connectionEpoch += 1;
|
|
4623
4688
|
if (this.reconnectTimer) {
|
|
4624
4689
|
clearTimeout(this.reconnectTimer);
|
|
4625
4690
|
this.reconnectTimer = null;
|
|
4626
4691
|
}
|
|
4627
4692
|
this.clearTokenRefreshTimer();
|
|
4628
4693
|
if (this.ws) {
|
|
4629
|
-
this.ws.close(1e3, "Client disconnect");
|
|
4694
|
+
this.ws.close(1e3, options.reason || "Client disconnect");
|
|
4630
4695
|
this.ws = null;
|
|
4631
4696
|
}
|
|
4632
4697
|
this.rejectPending(new Error("Client explicitly disconnected"));
|
|
@@ -4722,8 +4787,12 @@ function normalizePrompt(rawValue) {
|
|
|
4722
4787
|
const source = promptRecord || raw;
|
|
4723
4788
|
const id = typeof source.id === "string" ? source.id : typeof raw.id === "string" ? raw.id : typeof raw.promptId === "string" ? raw.promptId : "";
|
|
4724
4789
|
if (!id) return null;
|
|
4790
|
+
const jobId = typeof source.jobId === "string" && source.jobId.trim() ? source.jobId.trim() : typeof raw.jobId === "string" && raw.jobId.trim() ? raw.jobId.trim() : void 0;
|
|
4791
|
+
const turnId = typeof source.turnId === "string" && source.turnId.trim() ? source.turnId.trim() : typeof raw.turnId === "string" && raw.turnId.trim() ? raw.turnId.trim() : void 0;
|
|
4725
4792
|
return {
|
|
4726
4793
|
id,
|
|
4794
|
+
...jobId ? { jobId } : {},
|
|
4795
|
+
...turnId ? { turnId } : {},
|
|
4727
4796
|
type: normalizePromptType(source === raw ? raw : { ...raw, ...source }),
|
|
4728
4797
|
title: typeof source.title === "string" ? source.title : "Input required",
|
|
4729
4798
|
message: typeof source.message === "string" ? source.message : "",
|
|
@@ -4764,6 +4833,9 @@ function resolvePromptAnswer(prompt, answer) {
|
|
|
4764
4833
|
|
|
4765
4834
|
// src/session.ts
|
|
4766
4835
|
var PROMPT_TRANSCRIPT_APPEND_TIMEOUT_MS = 5e3;
|
|
4836
|
+
function toPascalCase(value) {
|
|
4837
|
+
return value.split(/[_:\-\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
4838
|
+
}
|
|
4767
4839
|
function withPromptTranscriptTimeout(promise) {
|
|
4768
4840
|
let timeout = null;
|
|
4769
4841
|
return Promise.race([
|
|
@@ -4807,6 +4879,9 @@ var Session = class {
|
|
|
4807
4879
|
this.initialQuota = options.initialQuota || null;
|
|
4808
4880
|
this.setupEventHandlers();
|
|
4809
4881
|
this.setupToolInvokeHandler();
|
|
4882
|
+
this.currentDomainRevision = this.extractDomainRevisionFromDoc(
|
|
4883
|
+
this.client.doc
|
|
4884
|
+
);
|
|
4810
4885
|
}
|
|
4811
4886
|
extractDomainRevisionFromDoc(doc) {
|
|
4812
4887
|
const domain = doc?.domain;
|
|
@@ -5326,9 +5401,7 @@ var Session = class {
|
|
|
5326
5401
|
if (classes && Object.keys(classes).length > 0) {
|
|
5327
5402
|
let docs2 = "# Domain Documentation\n\n";
|
|
5328
5403
|
docs2 += "Import concrete classes from `@granular/domain/<Class>` and global backend actions from `@granular/actions/backend`:\n\n";
|
|
5329
|
-
const classNames = Object.keys(classes).map(
|
|
5330
|
-
(c) => c.charAt(0).toUpperCase() + c.slice(1)
|
|
5331
|
-
);
|
|
5404
|
+
const classNames = Object.keys(classes).map(toPascalCase);
|
|
5332
5405
|
const globalNames = (globalTools || []).map((t) => t.name);
|
|
5333
5406
|
const importLines = [
|
|
5334
5407
|
...classNames.map(
|
|
@@ -5342,7 +5415,7 @@ ${importLines.join("\n") || "// No generated domain imports available."}
|
|
|
5342
5415
|
|
|
5343
5416
|
`;
|
|
5344
5417
|
for (const [className, cls] of Object.entries(classes)) {
|
|
5345
|
-
const TsName =
|
|
5418
|
+
const TsName = toPascalCase(className);
|
|
5346
5419
|
docs2 += `## ${TsName}
|
|
5347
5420
|
|
|
5348
5421
|
`;
|
|
@@ -5714,6 +5787,7 @@ function normalizeJobAgentMessageEnvelope(data) {
|
|
|
5714
5787
|
}
|
|
5715
5788
|
return {
|
|
5716
5789
|
jobId: d.jobId,
|
|
5790
|
+
...typeof d.turnId === "string" && d.turnId.trim() ? { turnId: d.turnId.trim() } : {},
|
|
5717
5791
|
message: {
|
|
5718
5792
|
messageId: d.messageId,
|
|
5719
5793
|
kind: d.kind === "artifacts" ? "artifacts" : "text",
|
|
@@ -6332,6 +6406,28 @@ function asString(value) {
|
|
|
6332
6406
|
function trimString(value) {
|
|
6333
6407
|
return typeof value === "string" ? value.trim() : "";
|
|
6334
6408
|
}
|
|
6409
|
+
function compactJson(value, maxLength = 320) {
|
|
6410
|
+
if (value === void 0 || value === null) return void 0;
|
|
6411
|
+
try {
|
|
6412
|
+
const json = JSON.stringify(value);
|
|
6413
|
+
if (!json || json === "undefined") return void 0;
|
|
6414
|
+
return json.length > maxLength ? `${json.slice(0, maxLength)}...` : json;
|
|
6415
|
+
} catch {
|
|
6416
|
+
return String(value);
|
|
6417
|
+
}
|
|
6418
|
+
}
|
|
6419
|
+
function artifactRecordsById(liveDoc) {
|
|
6420
|
+
const artifacts = asRecord3(liveDoc?.artifacts);
|
|
6421
|
+
const byId = asRecord3(artifacts?.byId) || {};
|
|
6422
|
+
return Object.fromEntries(
|
|
6423
|
+
Object.entries(byId).map(([artifactId, value]) => {
|
|
6424
|
+
const record = asRecord3(value);
|
|
6425
|
+
return record ? [artifactId, record] : null;
|
|
6426
|
+
}).filter(
|
|
6427
|
+
(entry) => Boolean(entry)
|
|
6428
|
+
)
|
|
6429
|
+
);
|
|
6430
|
+
}
|
|
6335
6431
|
function normalizeShowRefs(value) {
|
|
6336
6432
|
const record = asRecord3(value);
|
|
6337
6433
|
if (!record) return void 0;
|
|
@@ -6348,9 +6444,106 @@ function normalizeShowRefs(value) {
|
|
|
6348
6444
|
entryPaths: normalizeRefs(record.entryPaths),
|
|
6349
6445
|
listNames: normalizeRefs(record.listNames),
|
|
6350
6446
|
variableNames: normalizeRefs(record.variableNames),
|
|
6351
|
-
fileIds: normalizeRefs(record.fileIds)
|
|
6447
|
+
fileIds: normalizeRefs(record.fileIds),
|
|
6448
|
+
sessionArtifactIds: normalizeRefs(record.sessionArtifactIds),
|
|
6449
|
+
actionSuggestions: normalizeActionSuggestions(record.actionSuggestions),
|
|
6450
|
+
tables: Array.isArray(record.tables) ? record.tables.filter(
|
|
6451
|
+
(table) => Boolean(
|
|
6452
|
+
table && typeof table === "object" && !Array.isArray(table) && Array.isArray(table.columns) && Array.isArray(table.rows)
|
|
6453
|
+
)
|
|
6454
|
+
) : void 0
|
|
6352
6455
|
};
|
|
6353
|
-
return show.entryPaths || show.listNames || show.variableNames || show.fileIds ? show : void 0;
|
|
6456
|
+
return show.entryPaths || show.listNames || show.variableNames || show.fileIds || show.sessionArtifactIds || show.actionSuggestions || show.tables ? show : void 0;
|
|
6457
|
+
}
|
|
6458
|
+
function normalizeActionSuggestions(value) {
|
|
6459
|
+
if (!Array.isArray(value)) return void 0;
|
|
6460
|
+
const suggestions = [];
|
|
6461
|
+
for (const item of value) {
|
|
6462
|
+
const record = asRecord3(item);
|
|
6463
|
+
if (!record) continue;
|
|
6464
|
+
const label = trimString(record.label);
|
|
6465
|
+
if (!label) continue;
|
|
6466
|
+
const suggestionId = trimString(record.suggestionId) || trimString(record.id) || label;
|
|
6467
|
+
suggestions.push({
|
|
6468
|
+
suggestionId,
|
|
6469
|
+
label,
|
|
6470
|
+
...typeof record.description === "string" ? { description: record.description } : {},
|
|
6471
|
+
...asRecord3(record.artifact) ? { artifact: asRecord3(record.artifact) } : {},
|
|
6472
|
+
...asRecord3(record.target) ? { target: asRecord3(record.target) } : {},
|
|
6473
|
+
...asRecord3(record.metadata) ? { metadata: asRecord3(record.metadata) } : {}
|
|
6474
|
+
});
|
|
6475
|
+
}
|
|
6476
|
+
return suggestions.length ? suggestions : void 0;
|
|
6477
|
+
}
|
|
6478
|
+
var TRANSCRIPT_MESSAGE_PART_LIMIT = 128;
|
|
6479
|
+
var TRANSCRIPT_MESSAGE_PART_TEXT_LIMIT = 2e5;
|
|
6480
|
+
var TRANSCRIPT_MESSAGE_PART_ACTION_LABEL_LIMIT = 1e3;
|
|
6481
|
+
var TRANSCRIPT_MESSAGE_ACTION_LIMIT = 64;
|
|
6482
|
+
function normalizeConversationMessageActions(value) {
|
|
6483
|
+
if (!Array.isArray(value) || value.length === 0) return void 0;
|
|
6484
|
+
const actions = [];
|
|
6485
|
+
for (const item of value.slice(0, TRANSCRIPT_MESSAGE_ACTION_LIMIT)) {
|
|
6486
|
+
const record = asRecord3(item);
|
|
6487
|
+
const kind = record?.kind;
|
|
6488
|
+
const label = trimString(record?.label ?? record?.title);
|
|
6489
|
+
const status = record?.status;
|
|
6490
|
+
if (kind !== "frontend" && kind !== "backend" && kind !== "system" || !label || label.length > TRANSCRIPT_MESSAGE_PART_ACTION_LABEL_LIMIT) {
|
|
6491
|
+
continue;
|
|
6492
|
+
}
|
|
6493
|
+
actions.push({
|
|
6494
|
+
kind,
|
|
6495
|
+
label,
|
|
6496
|
+
...status === "done" || status === "queued" || status === "failed" ? { status } : {}
|
|
6497
|
+
});
|
|
6498
|
+
}
|
|
6499
|
+
return actions.length ? actions : void 0;
|
|
6500
|
+
}
|
|
6501
|
+
function normalizeConversationMessageParts(value, canonicalContent, canonicalActions) {
|
|
6502
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > TRANSCRIPT_MESSAGE_PART_LIMIT) {
|
|
6503
|
+
return void 0;
|
|
6504
|
+
}
|
|
6505
|
+
const parts = [];
|
|
6506
|
+
const canonicalActionsById = new Map(
|
|
6507
|
+
(canonicalActions || []).map((action) => [
|
|
6508
|
+
`${action.kind}:${action.label}`,
|
|
6509
|
+
action
|
|
6510
|
+
])
|
|
6511
|
+
);
|
|
6512
|
+
const seenActionIds = /* @__PURE__ */ new Set();
|
|
6513
|
+
let textLength = 0;
|
|
6514
|
+
for (const item of value) {
|
|
6515
|
+
const record = asRecord3(item);
|
|
6516
|
+
if (!record) return void 0;
|
|
6517
|
+
if (record.type === "text") {
|
|
6518
|
+
if (typeof record.text !== "string" || record.text.length === 0) {
|
|
6519
|
+
return void 0;
|
|
6520
|
+
}
|
|
6521
|
+
textLength += record.text.length;
|
|
6522
|
+
if (textLength > TRANSCRIPT_MESSAGE_PART_TEXT_LIMIT) return void 0;
|
|
6523
|
+
parts.push({ type: "text", text: record.text });
|
|
6524
|
+
continue;
|
|
6525
|
+
}
|
|
6526
|
+
if (record.type !== "action") return void 0;
|
|
6527
|
+
const action = asRecord3(record.action);
|
|
6528
|
+
const kind = action?.kind;
|
|
6529
|
+
const label = trimString(action?.label);
|
|
6530
|
+
if (kind !== "frontend" && kind !== "backend" && kind !== "system" || !label || label.length > TRANSCRIPT_MESSAGE_PART_ACTION_LABEL_LIMIT) {
|
|
6531
|
+
return void 0;
|
|
6532
|
+
}
|
|
6533
|
+
const actionId = `${kind}:${label}`;
|
|
6534
|
+
const canonicalAction = canonicalActionsById.get(actionId);
|
|
6535
|
+
if (!canonicalAction) return void 0;
|
|
6536
|
+
if (seenActionIds.has(actionId)) continue;
|
|
6537
|
+
seenActionIds.add(actionId);
|
|
6538
|
+
parts.push({
|
|
6539
|
+
type: "action",
|
|
6540
|
+
action: canonicalAction
|
|
6541
|
+
});
|
|
6542
|
+
}
|
|
6543
|
+
const orderedText = parts.filter(
|
|
6544
|
+
(part) => part.type === "text"
|
|
6545
|
+
).map((part) => part.text).join("");
|
|
6546
|
+
return orderedText === canonicalContent ? parts : void 0;
|
|
6354
6547
|
}
|
|
6355
6548
|
function stringifyTranscriptValue(value, fallback = "") {
|
|
6356
6549
|
if (typeof value === "string") {
|
|
@@ -6370,12 +6563,139 @@ function stringifyTranscriptValue(value, fallback = "") {
|
|
|
6370
6563
|
return String(value);
|
|
6371
6564
|
}
|
|
6372
6565
|
}
|
|
6373
|
-
function
|
|
6566
|
+
function latestInputEditSummary(metadata) {
|
|
6567
|
+
const lastInputEdit = asRecord3(metadata.lastInputEdit);
|
|
6568
|
+
if (!lastInputEdit) return null;
|
|
6569
|
+
const source = asString(lastInputEdit.source) || "unknown";
|
|
6570
|
+
const actor = asString(lastInputEdit.actorSubjectId) || asString(lastInputEdit.actorPermissionProfileName) || asString(lastInputEdit.jobId) || null;
|
|
6571
|
+
const inputKeys = Array.isArray(lastInputEdit.changedInputKeys) ? lastInputEdit.changedInputKeys.filter(
|
|
6572
|
+
(key) => typeof key === "string" && key.trim().length > 0
|
|
6573
|
+
).slice(0, 6) : [];
|
|
6574
|
+
const relationshipKeys = Array.isArray(lastInputEdit.changedRelationshipKeys) ? lastInputEdit.changedRelationshipKeys.filter(
|
|
6575
|
+
(key) => typeof key === "string" && key.trim().length > 0
|
|
6576
|
+
).slice(0, 6) : [];
|
|
6577
|
+
const changed = [
|
|
6578
|
+
inputKeys.length ? `inputs=${inputKeys.join(",")}` : null,
|
|
6579
|
+
relationshipKeys.length ? `relationships=${relationshipKeys.join(",")}` : null
|
|
6580
|
+
].filter(Boolean);
|
|
6581
|
+
return `lastEdit=${source}${actor ? ` by ${actor}` : ""}${changed.length ? ` (${changed.join("; ")})` : ""}`;
|
|
6582
|
+
}
|
|
6583
|
+
function artifactIssueSummary(record) {
|
|
6584
|
+
const validation = asRecord3(record.validation);
|
|
6585
|
+
if (!validation) return null;
|
|
6586
|
+
const issues = Array.isArray(validation.issues) ? validation.issues.map((issue) => asRecord3(issue)).filter((issue) => Boolean(issue)).slice(0, 3) : [];
|
|
6587
|
+
if (issues.length > 0) {
|
|
6588
|
+
return `issues=${issues.map((issue) => {
|
|
6589
|
+
const code = asString(issue.code) || asString(issue.kind) || "issue";
|
|
6590
|
+
const path2 = asString(issue.path);
|
|
6591
|
+
const message = trimString(issue.message);
|
|
6592
|
+
return `${code}${path2 ? ` at ${path2}` : ""}${message ? ` (${message})` : ""}`;
|
|
6593
|
+
}).join("; ")}`;
|
|
6594
|
+
}
|
|
6595
|
+
const error = trimString(validation.error) || trimString(validation.reason) || trimString(validation.message);
|
|
6596
|
+
return error ? `validation=${error}` : null;
|
|
6597
|
+
}
|
|
6598
|
+
function artifactExecutionSummary(metadata) {
|
|
6599
|
+
const execution = asRecord3(metadata.execution);
|
|
6600
|
+
if (!execution) return null;
|
|
6601
|
+
const result = asRecord3(execution.result);
|
|
6602
|
+
const awaiting = asString(result?.awaiting) || asString(execution.awaiting);
|
|
6603
|
+
const pendingTransition = asString(result?.pendingTransition) || asString(execution.pendingTransition);
|
|
6604
|
+
const approval = asRecord3(result?.approval) || asRecord3(execution.approval);
|
|
6605
|
+
const approvalTarget = asString(approval?.permissionProfileName) || asString(approval?.permissionProfileId) || asString(approval?.assigneeSubjectId);
|
|
6606
|
+
const error = trimString(execution.error);
|
|
6607
|
+
const pieces = [
|
|
6608
|
+
awaiting ? `awaiting=${awaiting}` : null,
|
|
6609
|
+
pendingTransition ? `pendingTransition=${pendingTransition}` : null,
|
|
6610
|
+
approvalTarget ? `approvalTarget=${approvalTarget}` : null,
|
|
6611
|
+
error ? `executionError=${error}` : null
|
|
6612
|
+
].filter(Boolean);
|
|
6613
|
+
return pieces.length ? pieces.join("; ") : null;
|
|
6614
|
+
}
|
|
6615
|
+
function artifactStatePathSummary(metadata) {
|
|
6616
|
+
const statePlan = asRecord3(metadata.statePlan);
|
|
6617
|
+
if (!statePlan) return null;
|
|
6618
|
+
const machineName = asString(statePlan.machineName);
|
|
6619
|
+
const targetState = asString(statePlan.targetState);
|
|
6620
|
+
const objectPath = asString(statePlan.objectPath);
|
|
6621
|
+
const approvedTransitions = Array.isArray(statePlan.approvedTransitions) ? statePlan.approvedTransitions.length : 0;
|
|
6622
|
+
const approvalDecisions = Array.isArray(statePlan.approvalDecisions) ? statePlan.approvalDecisions.length : 0;
|
|
6623
|
+
const pieces = [
|
|
6624
|
+
machineName || targetState ? `statePath=${machineName || "state_machine"}${targetState ? ` -> ${targetState}` : ""}` : null,
|
|
6625
|
+
objectPath ? `objectPath=${objectPath}` : null,
|
|
6626
|
+
approvedTransitions ? `approvedTransitions=${approvedTransitions}` : null,
|
|
6627
|
+
approvalDecisions ? `approvalDecisions=${approvalDecisions}` : null
|
|
6628
|
+
].filter(Boolean);
|
|
6629
|
+
return pieces.length ? pieces.join("; ") : null;
|
|
6630
|
+
}
|
|
6631
|
+
function artifactSummaryLine(artifactId, record) {
|
|
6632
|
+
if (!record) return `- ${artifactId}: unavailable in session artifact store`;
|
|
6633
|
+
const label = trimString(record.label) || artifactId;
|
|
6634
|
+
const kind = asString(record.kind) || "artifact";
|
|
6635
|
+
const status = asString(record.status) || "unknown";
|
|
6636
|
+
const createdByJobId = asString(record.createdByJobId);
|
|
6637
|
+
const target = asRecord3(record.target);
|
|
6638
|
+
const metadata = asRecord3(record.metadata) || {};
|
|
6639
|
+
const subArtifactIds = Array.isArray(record.subArtifactIds) ? record.subArtifactIds.filter(
|
|
6640
|
+
(id) => typeof id === "string" && id.trim().length > 0
|
|
6641
|
+
).slice(0, 8) : [];
|
|
6642
|
+
const relationships = compactJson(record.relationships, 220);
|
|
6643
|
+
const pieces = [
|
|
6644
|
+
`kind=${kind}`,
|
|
6645
|
+
`status=${status}`,
|
|
6646
|
+
createdByJobId ? `createdByJob=${createdByJobId}` : null,
|
|
6647
|
+
target ? `target=${asString(target.className) || "record"}:${asString(target.id) || "unknown"}${asString(target.label) ? ` (${asString(target.label)})` : ""}` : null,
|
|
6648
|
+
artifactStatePathSummary(metadata),
|
|
6649
|
+
artifactExecutionSummary(metadata),
|
|
6650
|
+
artifactIssueSummary(record),
|
|
6651
|
+
latestInputEditSummary(metadata),
|
|
6652
|
+
subArtifactIds.length ? `subArtifacts=${subArtifactIds.join(",")}` : null,
|
|
6653
|
+
relationships ? `relationships=${relationships}` : null
|
|
6654
|
+
].filter(Boolean);
|
|
6655
|
+
return `- ${artifactId}: ${label}${pieces.length ? `; ${pieces.join("; ")}` : ""}`;
|
|
6656
|
+
}
|
|
6657
|
+
function buildArtifactHistory(show, artifactsById) {
|
|
6374
6658
|
if (!show) return void 0;
|
|
6375
|
-
|
|
6659
|
+
const artifactIds = show.sessionArtifactIds || [];
|
|
6660
|
+
const actionSuggestions = show.actionSuggestions || [];
|
|
6661
|
+
if (artifactIds.length === 0 && actionSuggestions.length === 0) {
|
|
6662
|
+
return `[Agent message]
|
|
6376
6663
|
${stringifyTranscriptValue({ show }, "")}`;
|
|
6664
|
+
}
|
|
6665
|
+
const lines = artifactIds.slice(0, 8).map(
|
|
6666
|
+
(artifactId) => artifactSummaryLine(artifactId, artifactsById?.[artifactId])
|
|
6667
|
+
);
|
|
6668
|
+
if (artifactIds.length > 8) {
|
|
6669
|
+
lines.push(`- ${artifactIds.length - 8} more artifacts omitted`);
|
|
6670
|
+
}
|
|
6671
|
+
if (actionSuggestions.length > 0) {
|
|
6672
|
+
if (artifactIds.length > 0) lines.push("[Agent suggested actions]");
|
|
6673
|
+
for (const suggestion of actionSuggestions.slice(0, 8)) {
|
|
6674
|
+
lines.push(
|
|
6675
|
+
`- ${suggestion.label}${suggestion.description ? `; ${suggestion.description}` : ""}`
|
|
6676
|
+
);
|
|
6677
|
+
}
|
|
6678
|
+
if (actionSuggestions.length > 8) {
|
|
6679
|
+
lines.push(`- ${actionSuggestions.length - 8} more suggestions omitted`);
|
|
6680
|
+
}
|
|
6681
|
+
}
|
|
6682
|
+
const otherRefs = {
|
|
6683
|
+
entryPaths: show.entryPaths,
|
|
6684
|
+
listNames: show.listNames,
|
|
6685
|
+
variableNames: show.variableNames,
|
|
6686
|
+
fileIds: show.fileIds
|
|
6687
|
+
};
|
|
6688
|
+
const hasOtherRefs = Object.values(otherRefs).some(
|
|
6689
|
+
(value) => Array.isArray(value) && value.length > 0
|
|
6690
|
+
);
|
|
6691
|
+
const title = artifactIds.length > 0 ? "[Agent displayed session artifacts]" : "[Agent suggested actions]";
|
|
6692
|
+
return [
|
|
6693
|
+
title,
|
|
6694
|
+
...lines,
|
|
6695
|
+
hasOtherRefs ? `Other shown refs: ${stringifyTranscriptValue(otherRefs, "")}` : null
|
|
6696
|
+
].filter(Boolean).join("\n");
|
|
6377
6697
|
}
|
|
6378
|
-
function normalizeConversationMessage(raw) {
|
|
6698
|
+
function normalizeConversationMessage(raw, artifactsById) {
|
|
6379
6699
|
const record = asRecord3(raw);
|
|
6380
6700
|
if (!record) return null;
|
|
6381
6701
|
const role = record.role === "user" ? "user" : record.role === "assistant" ? "assistant" : null;
|
|
@@ -6383,10 +6703,18 @@ function normalizeConversationMessage(raw) {
|
|
|
6383
6703
|
const content = trimString(
|
|
6384
6704
|
record.content ?? record.reply ?? record.message ?? record.text
|
|
6385
6705
|
);
|
|
6706
|
+
const actions = role === "assistant" ? normalizeConversationMessageActions(record.actions) : void 0;
|
|
6707
|
+
const parts = role === "assistant" ? normalizeConversationMessageParts(record.parts, content, actions) : void 0;
|
|
6386
6708
|
const show = normalizeShowRefs(record.show);
|
|
6387
6709
|
const id = asString(record.id) || crypto.randomUUID();
|
|
6388
6710
|
const timestamp = asNumber(record.timestamp) || asNumber(record.ts) || 0;
|
|
6389
|
-
if (!content && !show) return null;
|
|
6711
|
+
if (!content && !show && !actions?.length) return null;
|
|
6712
|
+
const artifactHistory = buildArtifactHistory(show, artifactsById);
|
|
6713
|
+
const historyContent = role === "assistant" ? content && artifactHistory ? `[Assistant reply]
|
|
6714
|
+
${content}
|
|
6715
|
+
|
|
6716
|
+
${artifactHistory}` : content ? `[Assistant reply]
|
|
6717
|
+
${content}` : artifactHistory : void 0;
|
|
6390
6718
|
return {
|
|
6391
6719
|
id,
|
|
6392
6720
|
role,
|
|
@@ -6395,8 +6723,9 @@ function normalizeConversationMessage(raw) {
|
|
|
6395
6723
|
jobId: asString(record.jobId),
|
|
6396
6724
|
promptId: asString(record.promptId),
|
|
6397
6725
|
show,
|
|
6398
|
-
|
|
6399
|
-
|
|
6726
|
+
actions,
|
|
6727
|
+
parts,
|
|
6728
|
+
historyContent,
|
|
6400
6729
|
source: "conversation"
|
|
6401
6730
|
};
|
|
6402
6731
|
}
|
|
@@ -6439,7 +6768,7 @@ ${assistantContent}`,
|
|
|
6439
6768
|
return entries;
|
|
6440
6769
|
});
|
|
6441
6770
|
}
|
|
6442
|
-
function normalizeAgentMessageEntries(jobId, rawMessages) {
|
|
6771
|
+
function normalizeAgentMessageEntries(jobId, rawMessages, artifactsById) {
|
|
6443
6772
|
return asArray(rawMessages).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
|
|
6444
6773
|
(left, right) => (asNumber(left.timestamp) || asNumber(left.ts) || 0) - (asNumber(right.timestamp) || asNumber(right.ts) || 0)
|
|
6445
6774
|
).flatMap((message) => {
|
|
@@ -6470,14 +6799,14 @@ ${reply}`,
|
|
|
6470
6799
|
timestamp,
|
|
6471
6800
|
jobId,
|
|
6472
6801
|
show,
|
|
6473
|
-
historyContent: buildArtifactHistory(show),
|
|
6802
|
+
historyContent: buildArtifactHistory(show, artifactsById),
|
|
6474
6803
|
source: "job_agent_message"
|
|
6475
6804
|
});
|
|
6476
6805
|
}
|
|
6477
6806
|
return entries;
|
|
6478
6807
|
});
|
|
6479
6808
|
}
|
|
6480
|
-
function buildJobFallbackEntries(jobId, job, sessionHeap) {
|
|
6809
|
+
function buildJobFallbackEntries(jobId, job, sessionHeap, artifactsById) {
|
|
6481
6810
|
const timestamp = asNumber(job.finishedAt) || asNumber(job.startedAt) || asNumber(job.submittedAt) || 0;
|
|
6482
6811
|
const resultPreview = stringifyTranscriptValue(
|
|
6483
6812
|
job.result,
|
|
@@ -6515,7 +6844,7 @@ ${responseText}`,
|
|
|
6515
6844
|
timestamp,
|
|
6516
6845
|
jobId,
|
|
6517
6846
|
show,
|
|
6518
|
-
historyContent: buildArtifactHistory(show),
|
|
6847
|
+
historyContent: buildArtifactHistory(show, artifactsById),
|
|
6519
6848
|
source: "job_result"
|
|
6520
6849
|
});
|
|
6521
6850
|
}
|
|
@@ -6569,10 +6898,11 @@ function buildJobCodeEntry(jobId, job) {
|
|
|
6569
6898
|
function buildSessionTranscript(input) {
|
|
6570
6899
|
const liveDoc = input.liveDoc || null;
|
|
6571
6900
|
const sessionHeap = input.sessionHeap || EMPTY_HEAP;
|
|
6901
|
+
const artifactsById = artifactRecordsById(liveDoc);
|
|
6572
6902
|
const transcript = [];
|
|
6573
6903
|
const conversationMessages = asArray(
|
|
6574
6904
|
asRecord3(liveDoc?.conversation)?.messages
|
|
6575
|
-
).map((message) => normalizeConversationMessage(message)).filter((message) => Boolean(message));
|
|
6905
|
+
).map((message) => normalizeConversationMessage(message, artifactsById)).filter((message) => Boolean(message));
|
|
6576
6906
|
const conversationPromptIds = new Set(
|
|
6577
6907
|
conversationMessages.map((message) => message.promptId).filter((promptId) => Boolean(promptId))
|
|
6578
6908
|
);
|
|
@@ -6599,7 +6929,8 @@ function buildSessionTranscript(input) {
|
|
|
6599
6929
|
if (!assistantConversationJobIds.has(jobId)) {
|
|
6600
6930
|
const agentEntries = normalizeAgentMessageEntries(
|
|
6601
6931
|
jobId,
|
|
6602
|
-
job.agentMessages
|
|
6932
|
+
job.agentMessages,
|
|
6933
|
+
artifactsById
|
|
6603
6934
|
);
|
|
6604
6935
|
if (agentEntries.length > 0) {
|
|
6605
6936
|
transcript.push(...agentEntries);
|
|
@@ -6608,7 +6939,8 @@ function buildSessionTranscript(input) {
|
|
|
6608
6939
|
...buildJobFallbackEntries(
|
|
6609
6940
|
jobId,
|
|
6610
6941
|
job,
|
|
6611
|
-
sessionHeap
|
|
6942
|
+
sessionHeap,
|
|
6943
|
+
artifactsById
|
|
6612
6944
|
)
|
|
6613
6945
|
);
|
|
6614
6946
|
}
|
|
@@ -10774,16 +11106,107 @@ var StateMachineStateSchema = external_exports.union([
|
|
|
10774
11106
|
external_exports.string(),
|
|
10775
11107
|
external_exports.object({
|
|
10776
11108
|
name: external_exports.string().min(1),
|
|
11109
|
+
label: external_exports.string().optional(),
|
|
11110
|
+
description: external_exports.string().optional(),
|
|
10777
11111
|
isFinal: external_exports.boolean().optional()
|
|
10778
11112
|
}).strict()
|
|
10779
11113
|
]);
|
|
11114
|
+
var StateTransitionInputBindingSchema = external_exports.lazy(
|
|
11115
|
+
() => external_exports.union([
|
|
11116
|
+
external_exports.null(),
|
|
11117
|
+
external_exports.string(),
|
|
11118
|
+
external_exports.number(),
|
|
11119
|
+
external_exports.boolean(),
|
|
11120
|
+
external_exports.array(StateTransitionInputBindingSchema),
|
|
11121
|
+
external_exports.object({
|
|
11122
|
+
const: external_exports.unknown()
|
|
11123
|
+
}).strict(),
|
|
11124
|
+
external_exports.object({
|
|
11125
|
+
from: external_exports.literal("object"),
|
|
11126
|
+
path: external_exports.string().min(1),
|
|
11127
|
+
editable: external_exports.boolean().optional()
|
|
11128
|
+
}).strict(),
|
|
11129
|
+
external_exports.object({
|
|
11130
|
+
from: external_exports.literal("field"),
|
|
11131
|
+
name: external_exports.string().min(1),
|
|
11132
|
+
editable: external_exports.boolean().optional()
|
|
11133
|
+
}).strict(),
|
|
11134
|
+
external_exports.object({
|
|
11135
|
+
from: external_exports.literal("relationship"),
|
|
11136
|
+
name: external_exports.string().min(1),
|
|
11137
|
+
path: external_exports.string().min(1).optional(),
|
|
11138
|
+
many: external_exports.boolean().optional(),
|
|
11139
|
+
editable: external_exports.boolean().optional()
|
|
11140
|
+
}).strict(),
|
|
11141
|
+
external_exports.object({
|
|
11142
|
+
from: external_exports.literal("session"),
|
|
11143
|
+
path: external_exports.string().min(1),
|
|
11144
|
+
editable: external_exports.boolean().optional()
|
|
11145
|
+
}).strict(),
|
|
11146
|
+
external_exports.object({
|
|
11147
|
+
from: external_exports.literal("actor"),
|
|
11148
|
+
path: external_exports.string().min(1),
|
|
11149
|
+
editable: external_exports.boolean().optional()
|
|
11150
|
+
}).strict(),
|
|
11151
|
+
external_exports.record(external_exports.string(), StateTransitionInputBindingSchema)
|
|
11152
|
+
])
|
|
11153
|
+
);
|
|
11154
|
+
var StateTransitionActionSchema = external_exports.object({
|
|
11155
|
+
effect: external_exports.string().min(1),
|
|
11156
|
+
input: external_exports.record(external_exports.string(), StateTransitionInputBindingSchema).optional()
|
|
11157
|
+
}).strict();
|
|
11158
|
+
var StateTransitionAssigneeSchema = external_exports.object({
|
|
11159
|
+
kind: external_exports.string().min(1),
|
|
11160
|
+
from: StateTransitionInputBindingSchema.optional(),
|
|
11161
|
+
role: external_exports.string().optional(),
|
|
11162
|
+
label: external_exports.string().optional()
|
|
11163
|
+
}).strict();
|
|
11164
|
+
var StateTransitionRelatedStateRequirementSchema = external_exports.object({
|
|
11165
|
+
relationship: external_exports.string().min(1),
|
|
11166
|
+
machine: external_exports.string().min(1),
|
|
11167
|
+
state: external_exports.string().min(1),
|
|
11168
|
+
className: external_exports.string().min(1).optional(),
|
|
11169
|
+
label: external_exports.string().optional(),
|
|
11170
|
+
mode: external_exports.enum(["every", "some", "any"]).optional()
|
|
11171
|
+
}).strict();
|
|
11172
|
+
var StateTransitionRequirementsSchema = external_exports.object({
|
|
11173
|
+
fields: external_exports.array(external_exports.string().min(1)).optional(),
|
|
11174
|
+
relationships: external_exports.array(external_exports.string().min(1)).optional(),
|
|
11175
|
+
relatedStates: external_exports.array(StateTransitionRelatedStateRequirementSchema).optional()
|
|
11176
|
+
}).strict();
|
|
11177
|
+
var StateTransitionPermissionSchema = external_exports.union([
|
|
11178
|
+
external_exports.string().min(1),
|
|
11179
|
+
external_exports.object({
|
|
11180
|
+
profile: external_exports.string().min(1).optional(),
|
|
11181
|
+
profileId: external_exports.string().min(1).optional(),
|
|
11182
|
+
label: external_exports.string().optional(),
|
|
11183
|
+
reason: external_exports.string().optional()
|
|
11184
|
+
}).strict()
|
|
11185
|
+
]);
|
|
11186
|
+
var StateTransitionExpectedOutcomeSchema = external_exports.union([
|
|
11187
|
+
external_exports.string().min(1),
|
|
11188
|
+
external_exports.object({
|
|
11189
|
+
machine: external_exports.string().min(1).optional(),
|
|
11190
|
+
state: external_exports.string().min(1),
|
|
11191
|
+
summary: external_exports.string().optional()
|
|
11192
|
+
}).strict()
|
|
11193
|
+
]);
|
|
10780
11194
|
var StateMachineTransitionSchema = external_exports.object({
|
|
10781
11195
|
name: external_exports.string().min(1),
|
|
10782
11196
|
from: external_exports.string().min(1),
|
|
10783
|
-
to: external_exports.string().min(1)
|
|
11197
|
+
to: external_exports.string().min(1),
|
|
11198
|
+
label: external_exports.string().optional(),
|
|
11199
|
+
description: external_exports.string().optional(),
|
|
11200
|
+
action: StateTransitionActionSchema.optional(),
|
|
11201
|
+
assignee: StateTransitionAssigneeSchema.optional(),
|
|
11202
|
+
requirements: StateTransitionRequirementsSchema.optional(),
|
|
11203
|
+
permission: StateTransitionPermissionSchema.optional(),
|
|
11204
|
+
risk: external_exports.enum(["low", "medium", "high"]).optional(),
|
|
11205
|
+
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional()
|
|
10784
11206
|
}).strict();
|
|
10785
11207
|
external_exports.object({
|
|
10786
11208
|
name: external_exports.string().min(1),
|
|
11209
|
+
stateField: external_exports.string().min(1).optional(),
|
|
10787
11210
|
entryState: external_exports.string().min(1),
|
|
10788
11211
|
states: external_exports.array(StateMachineStateSchema).min(1),
|
|
10789
11212
|
transitions: external_exports.array(StateMachineTransitionSchema),
|
|
@@ -10850,6 +11273,16 @@ var PoliciesSchema = external_exports.object({
|
|
|
10850
11273
|
confirmWhen: external_exports.array(PolicyRuleSchema).optional(),
|
|
10851
11274
|
denyWhen: external_exports.array(PolicyRuleSchema).optional()
|
|
10852
11275
|
}).strict();
|
|
11276
|
+
var CreatesSchema = external_exports.union([
|
|
11277
|
+
external_exports.string().min(1),
|
|
11278
|
+
external_exports.object({
|
|
11279
|
+
className: external_exports.string().min(1),
|
|
11280
|
+
idPath: external_exports.string().min(1).optional(),
|
|
11281
|
+
pathPath: external_exports.string().min(1).optional(),
|
|
11282
|
+
statePath: external_exports.string().min(1).optional(),
|
|
11283
|
+
classStateHandle: external_exports.boolean().optional()
|
|
11284
|
+
}).strict()
|
|
11285
|
+
]);
|
|
10853
11286
|
external_exports.object({
|
|
10854
11287
|
postCondition: external_exports.union([
|
|
10855
11288
|
external_exports.string(),
|
|
@@ -10880,6 +11313,7 @@ external_exports.object({
|
|
|
10880
11313
|
mode: external_exports.string().optional()
|
|
10881
11314
|
}).strict()
|
|
10882
11315
|
]).optional(),
|
|
11316
|
+
creates: CreatesSchema.optional(),
|
|
10883
11317
|
access: external_exports.enum(["read", "write", "ui"]).optional(),
|
|
10884
11318
|
effectKind: external_exports.enum(["read", "write", "ui"]).optional(),
|
|
10885
11319
|
sideEffect: external_exports.enum(["read", "write", "ui", "readonly", "read_only"]).optional(),
|
|
@@ -11107,9 +11541,10 @@ function mergeMethodSummaryPatch(target, patch) {
|
|
|
11107
11541
|
if (patch.metamodels !== void 0) target.metamodels = patch.metamodels;
|
|
11108
11542
|
if (patch.effectBehaviors !== void 0)
|
|
11109
11543
|
target.effectBehaviors = patch.effectBehaviors;
|
|
11544
|
+
if (patch.creates !== void 0) target.creates = patch.creates;
|
|
11110
11545
|
if (patch.static !== void 0) target.static = patch.static;
|
|
11111
11546
|
}
|
|
11112
|
-
function
|
|
11547
|
+
function toPascalCase2(value) {
|
|
11113
11548
|
return value.split(/[_:\-\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
11114
11549
|
}
|
|
11115
11550
|
function normalizeNotesInput(input) {
|
|
@@ -11167,29 +11602,60 @@ function normalizeEffectBehaviorSummary(metamodels) {
|
|
|
11167
11602
|
}
|
|
11168
11603
|
return Object.keys(result).length > 0 ? result : null;
|
|
11169
11604
|
}
|
|
11170
|
-
function
|
|
11171
|
-
if (!
|
|
11172
|
-
|
|
11605
|
+
function normalizeCreationSummary(metamodels) {
|
|
11606
|
+
if (!isObject(metamodels)) return null;
|
|
11607
|
+
let raw = metamodels.creates;
|
|
11608
|
+
if (typeof raw === "string" && raw.trim().length > 0) {
|
|
11609
|
+
const trimmed = raw.trim();
|
|
11610
|
+
if (trimmed.startsWith("{") || trimmed.startsWith('"')) {
|
|
11611
|
+
try {
|
|
11612
|
+
raw = JSON.parse(trimmed);
|
|
11613
|
+
} catch {
|
|
11614
|
+
return { className: trimmed };
|
|
11615
|
+
}
|
|
11616
|
+
} else {
|
|
11617
|
+
return { className: trimmed };
|
|
11618
|
+
}
|
|
11619
|
+
}
|
|
11620
|
+
if (typeof raw === "string" && raw.trim().length > 0) {
|
|
11621
|
+
return { className: raw.trim() };
|
|
11173
11622
|
}
|
|
11623
|
+
if (!isObject(raw)) return null;
|
|
11624
|
+
const className = typeof raw.className === "string" && raw.className.trim() ? raw.className.trim() : "";
|
|
11625
|
+
if (!className) return null;
|
|
11626
|
+
return {
|
|
11627
|
+
className,
|
|
11628
|
+
...typeof raw.idPath === "string" && raw.idPath.trim() ? { idPath: raw.idPath.trim() } : {},
|
|
11629
|
+
...typeof raw.pathPath === "string" && raw.pathPath.trim() ? { pathPath: raw.pathPath.trim() } : {},
|
|
11630
|
+
...typeof raw.statePath === "string" && raw.statePath.trim() ? { statePath: raw.statePath.trim() } : {},
|
|
11631
|
+
...typeof raw.classStateHandle === "boolean" ? { classStateHandle: raw.classStateHandle } : {}
|
|
11632
|
+
};
|
|
11633
|
+
}
|
|
11634
|
+
function buildEffectBehaviorDocs(effectBehaviors, creates) {
|
|
11174
11635
|
const docs = [];
|
|
11175
|
-
if (
|
|
11636
|
+
if (creates) {
|
|
11637
|
+
docs.push(
|
|
11638
|
+
`Creation method: creates ${creates.className}. The agent may use generated class-level new-record action methods for this class.`
|
|
11639
|
+
);
|
|
11640
|
+
}
|
|
11641
|
+
if (effectBehaviors?.approvalRequired?.required) {
|
|
11176
11642
|
docs.push(
|
|
11177
11643
|
effectBehaviors.approvalRequired.reason ? `Approval required: ${effectBehaviors.approvalRequired.reason}.` : "Approval required before execution."
|
|
11178
11644
|
);
|
|
11179
11645
|
}
|
|
11180
|
-
if (effectBehaviors
|
|
11646
|
+
if (effectBehaviors?.postCondition) {
|
|
11181
11647
|
docs.push(`Post-condition: ${effectBehaviors.postCondition.condition}.`);
|
|
11182
11648
|
if (effectBehaviors.postCondition.description) {
|
|
11183
11649
|
docs.push(effectBehaviors.postCondition.description);
|
|
11184
11650
|
}
|
|
11185
11651
|
}
|
|
11186
|
-
if (effectBehaviors
|
|
11652
|
+
if (effectBehaviors?.dryRun?.enabled) {
|
|
11187
11653
|
docs.push("Supports dry run.");
|
|
11188
11654
|
if (effectBehaviors.dryRun.description) {
|
|
11189
11655
|
docs.push(effectBehaviors.dryRun.description);
|
|
11190
11656
|
}
|
|
11191
11657
|
}
|
|
11192
|
-
if (effectBehaviors
|
|
11658
|
+
if (effectBehaviors?.reverse) {
|
|
11193
11659
|
if (effectBehaviors.reverse.handler) {
|
|
11194
11660
|
docs.push(`Reverse handler: ${effectBehaviors.reverse.handler}.`);
|
|
11195
11661
|
} else {
|
|
@@ -11248,13 +11714,21 @@ function buildEffectBehaviorMutations(toolPath, spec) {
|
|
|
11248
11714
|
query: `mutation { at(path: ${JSON.stringify(toolPath)}) { set_approval_required(${args}) { kind } } }`
|
|
11249
11715
|
});
|
|
11250
11716
|
}
|
|
11717
|
+
if (spec.creates !== void 0) {
|
|
11718
|
+
mutations.push({
|
|
11719
|
+
label: `set creates on ${toolPath}`,
|
|
11720
|
+
query: `mutation { at(path: ${JSON.stringify(toolPath)}) { create_submodel(subpath: "creates", label: "creates") { set_string_value(value: ${JSON.stringify(
|
|
11721
|
+
JSON.stringify(spec.creates)
|
|
11722
|
+
)}) { done } } } }`
|
|
11723
|
+
});
|
|
11724
|
+
}
|
|
11251
11725
|
return mutations;
|
|
11252
11726
|
}
|
|
11253
11727
|
function readMethodEffectBehaviors(rawMethod) {
|
|
11728
|
+
const metamodels = isObject(rawMethod.metamodels) ? rawMethod.metamodels : null;
|
|
11254
11729
|
return {
|
|
11255
|
-
effectBehaviors: normalizeEffectBehaviorSummary(
|
|
11256
|
-
|
|
11257
|
-
)
|
|
11730
|
+
effectBehaviors: normalizeEffectBehaviorSummary(metamodels),
|
|
11731
|
+
creates: normalizeCreationSummary(metamodels)
|
|
11258
11732
|
};
|
|
11259
11733
|
}
|
|
11260
11734
|
var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
@@ -11276,6 +11750,10 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
11276
11750
|
{
|
|
11277
11751
|
key: "approvalRequired",
|
|
11278
11752
|
description: "Boolean or `{ required, reason, mode }`."
|
|
11753
|
+
},
|
|
11754
|
+
{
|
|
11755
|
+
key: "creates",
|
|
11756
|
+
description: 'Marks a static method as an allowed creator for a class. Use `creates: "class_name"` or `{ className, idPath, pathPath, statePath, classStateHandle }`.'
|
|
11279
11757
|
}
|
|
11280
11758
|
]
|
|
11281
11759
|
},
|
|
@@ -11395,7 +11873,10 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
11395
11873
|
...methodIR,
|
|
11396
11874
|
docs: [
|
|
11397
11875
|
...methodIR.docs,
|
|
11398
|
-
...buildEffectBehaviorDocs(
|
|
11876
|
+
...buildEffectBehaviorDocs(
|
|
11877
|
+
methodSummary.effectBehaviors,
|
|
11878
|
+
methodSummary.creates
|
|
11879
|
+
)
|
|
11399
11880
|
]
|
|
11400
11881
|
};
|
|
11401
11882
|
}
|
|
@@ -11636,15 +12117,50 @@ function toRecordSearchResult(className, node) {
|
|
|
11636
12117
|
return [];
|
|
11637
12118
|
}
|
|
11638
12119
|
) : [];
|
|
12120
|
+
const graphPathId = extractRecordIdFromGraphPath(path2, className);
|
|
12121
|
+
const realIdField = fields.find(
|
|
12122
|
+
(field) => normalizeGraphPathSegment(field.name) === "real_id" && typeof field.value === "string" && field.value.trim()
|
|
12123
|
+
);
|
|
12124
|
+
const id = typeof realIdField?.value === "string" ? realIdField.value.trim() : graphPathId;
|
|
12125
|
+
const rawLabel = typeof node.label === "string" && node.label.trim() ? node.label : "";
|
|
12126
|
+
if (fields.length === 0 && rawLabel && isPlaceholderRecordLabel(rawLabel, graphPathId, path2)) {
|
|
12127
|
+
return null;
|
|
12128
|
+
}
|
|
12129
|
+
const fallbackLabel = displayLabelFromFields(fields);
|
|
12130
|
+
const label = rawLabel && !isPlaceholderRecordLabel(rawLabel, id, path2) ? rawLabel : fallbackLabel || rawLabel || id;
|
|
11639
12131
|
return {
|
|
11640
12132
|
path: path2,
|
|
11641
12133
|
className,
|
|
11642
|
-
id
|
|
11643
|
-
label
|
|
12134
|
+
id,
|
|
12135
|
+
label,
|
|
11644
12136
|
description: typeof node.description === "string" && node.description.trim() ? node.description : null,
|
|
11645
12137
|
fields
|
|
11646
12138
|
};
|
|
11647
12139
|
}
|
|
12140
|
+
function isPlaceholderRecordLabel(label, id, path2) {
|
|
12141
|
+
const normalizedLabel = normalizeGraphPathSegment(label);
|
|
12142
|
+
return normalizedLabel === normalizeGraphPathSegment(id) || normalizedLabel === normalizeGraphPathSegment(path2);
|
|
12143
|
+
}
|
|
12144
|
+
function displayLabelFromFields(fields) {
|
|
12145
|
+
const preferredFieldNames = [
|
|
12146
|
+
"name",
|
|
12147
|
+
"title",
|
|
12148
|
+
"label",
|
|
12149
|
+
"display_name",
|
|
12150
|
+
"file_name",
|
|
12151
|
+
"number",
|
|
12152
|
+
"code"
|
|
12153
|
+
];
|
|
12154
|
+
for (const preferred of preferredFieldNames) {
|
|
12155
|
+
const match = fields.find(
|
|
12156
|
+
(field) => normalizeGraphPathSegment(field.name) === preferred && typeof field.value === "string" && field.value.trim()
|
|
12157
|
+
);
|
|
12158
|
+
if (typeof match?.value === "string") {
|
|
12159
|
+
return match.value.trim();
|
|
12160
|
+
}
|
|
12161
|
+
}
|
|
12162
|
+
return null;
|
|
12163
|
+
}
|
|
11648
12164
|
function normalizeRecordSearchText(value) {
|
|
11649
12165
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
|
|
11650
12166
|
}
|
|
@@ -11786,7 +12302,12 @@ async function recordOpenAIUsageSpend(options) {
|
|
|
11786
12302
|
const metadata = {
|
|
11787
12303
|
...options.metadata || {},
|
|
11788
12304
|
...options.usage.rawUsage !== void 0 ? { openaiUsage: options.usage.rawUsage } : {},
|
|
11789
|
-
usageContext: context
|
|
12305
|
+
usageContext: context,
|
|
12306
|
+
pricingContextTier: options.usage.pricingContextTier,
|
|
12307
|
+
cacheWritePricePerMillionMicros: options.usage.cacheWritePricePerMillionMicros,
|
|
12308
|
+
cacheWriteTokens: options.usage.cacheWriteTokens,
|
|
12309
|
+
cacheWriteCostMicros: options.usage.cacheWriteCostMicros,
|
|
12310
|
+
longContextThresholdTokens: options.usage.longContextThresholdTokens
|
|
11790
12311
|
};
|
|
11791
12312
|
const response = await fetch(
|
|
11792
12313
|
`${toGranularHttpBase(options.apiUrl)}/control/spend/events`,
|
|
@@ -12544,15 +13065,47 @@ var searchableMetamodelPackage = defineMetamodelPackage({
|
|
|
12544
13065
|
|
|
12545
13066
|
// ../metamodel-state-machine/src/index.ts
|
|
12546
13067
|
function normalizeStateMachines(values) {
|
|
13068
|
+
const parseJsonRecord = (value) => {
|
|
13069
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
13070
|
+
return value;
|
|
13071
|
+
}
|
|
13072
|
+
if (typeof value !== "string" || !value.trim()) return null;
|
|
13073
|
+
try {
|
|
13074
|
+
const parsed = JSON.parse(value);
|
|
13075
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
13076
|
+
} catch {
|
|
13077
|
+
return null;
|
|
13078
|
+
}
|
|
13079
|
+
};
|
|
13080
|
+
const parseJsonValue = (value) => {
|
|
13081
|
+
if (value === null || typeof value === "undefined") return null;
|
|
13082
|
+
if (typeof value !== "string") return value;
|
|
13083
|
+
if (!value.trim()) return null;
|
|
13084
|
+
try {
|
|
13085
|
+
return JSON.parse(value);
|
|
13086
|
+
} catch {
|
|
13087
|
+
return value;
|
|
13088
|
+
}
|
|
13089
|
+
};
|
|
12547
13090
|
return (values || []).map((machine) => {
|
|
12548
13091
|
const states = (machine?.states || []).map((state) => ({
|
|
12549
13092
|
name: String(state?.name || ""),
|
|
12550
|
-
|
|
13093
|
+
label: typeof state?.label === "string" ? state.label : null,
|
|
13094
|
+
description: typeof state?.description === "string" ? state.description : null,
|
|
13095
|
+
isFinal: Boolean(state?.is_final ?? state?.isFinal)
|
|
12551
13096
|
})).filter((state) => state.name.length > 0);
|
|
12552
13097
|
const transitions = (machine?.transitions || []).map((transition) => ({
|
|
12553
13098
|
name: String(transition?.name || ""),
|
|
12554
13099
|
from: String(transition?.from?.name || ""),
|
|
12555
|
-
to: String(transition?.to?.name || "")
|
|
13100
|
+
to: String(transition?.to?.name || ""),
|
|
13101
|
+
label: typeof transition?.label === "string" ? transition.label : null,
|
|
13102
|
+
description: typeof transition?.description === "string" ? transition.description : null,
|
|
13103
|
+
action: parseJsonRecord(transition?.action) || parseJsonRecord(transition?.action_json),
|
|
13104
|
+
assignee: parseJsonRecord(transition?.assignee) || parseJsonRecord(transition?.assignee_json),
|
|
13105
|
+
requirements: parseJsonRecord(transition?.requirements) || parseJsonRecord(transition?.requirements_json),
|
|
13106
|
+
permission: parseJsonValue(transition?.permission) ?? parseJsonValue(transition?.permission_json),
|
|
13107
|
+
risk: transition?.risk === "low" || transition?.risk === "medium" || transition?.risk === "high" ? transition.risk : null,
|
|
13108
|
+
expectedOutcome: parseJsonValue(transition?.expectedOutcome) ?? parseJsonValue(transition?.expected_outcome_json)
|
|
12556
13109
|
})).filter(
|
|
12557
13110
|
(transition) => transition.name.length > 0 && transition.from.length > 0 && transition.to.length > 0
|
|
12558
13111
|
);
|
|
@@ -12566,7 +13119,7 @@ function normalizeStateMachines(values) {
|
|
|
12566
13119
|
}).filter((machine) => machine.name.length > 0);
|
|
12567
13120
|
}
|
|
12568
13121
|
function stateTypeName(className, machineName) {
|
|
12569
|
-
return `${
|
|
13122
|
+
return `${toPascalCase2(className)}${toPascalCase2(machineName)}`;
|
|
12570
13123
|
}
|
|
12571
13124
|
function transitionTypeName(className, machineName) {
|
|
12572
13125
|
return `${stateTypeName(className, machineName)}Transition`;
|
|
@@ -12574,6 +13127,15 @@ function transitionTypeName(className, machineName) {
|
|
|
12574
13127
|
function pathTypeName(className, machineName) {
|
|
12575
13128
|
return `${stateTypeName(className, machineName)}Path`;
|
|
12576
13129
|
}
|
|
13130
|
+
function methodToken(value) {
|
|
13131
|
+
const token = String(value || "").trim().replace(/[^A-Za-z0-9_]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
13132
|
+
return token || "state";
|
|
13133
|
+
}
|
|
13134
|
+
function transitionActionsForMachine(machine) {
|
|
13135
|
+
return Object.fromEntries(
|
|
13136
|
+
(machine.transitions || []).filter((transition) => transition.action?.effect).map((transition) => [transition.name, transition.action])
|
|
13137
|
+
);
|
|
13138
|
+
}
|
|
12577
13139
|
function normalizeStateDefinitions(machine) {
|
|
12578
13140
|
const finalStates = new Set(machine.finalStates || []);
|
|
12579
13141
|
const states = /* @__PURE__ */ new Map();
|
|
@@ -12587,6 +13149,8 @@ function normalizeStateDefinitions(machine) {
|
|
|
12587
13149
|
}
|
|
12588
13150
|
states.set(rawState.name, {
|
|
12589
13151
|
name: rawState.name,
|
|
13152
|
+
label: rawState.label,
|
|
13153
|
+
description: rawState.description,
|
|
12590
13154
|
isFinal: Boolean(rawState.isFinal) || finalStates.has(rawState.name)
|
|
12591
13155
|
});
|
|
12592
13156
|
}
|
|
@@ -12598,6 +13162,44 @@ function normalizeStateDefinitions(machine) {
|
|
|
12598
13162
|
}
|
|
12599
13163
|
return [...states.values()];
|
|
12600
13164
|
}
|
|
13165
|
+
function transitionMetadataGraphqlArgs(transition) {
|
|
13166
|
+
const args = [];
|
|
13167
|
+
if (typeof transition.label === "string") {
|
|
13168
|
+
args.push(`label: ${JSON.stringify(transition.label)}`);
|
|
13169
|
+
}
|
|
13170
|
+
if (typeof transition.description === "string") {
|
|
13171
|
+
args.push(`description: ${JSON.stringify(transition.description)}`);
|
|
13172
|
+
}
|
|
13173
|
+
if (transition.action) {
|
|
13174
|
+
args.push(
|
|
13175
|
+
`action_json: ${JSON.stringify(JSON.stringify(transition.action))}`
|
|
13176
|
+
);
|
|
13177
|
+
}
|
|
13178
|
+
if (transition.assignee) {
|
|
13179
|
+
args.push(
|
|
13180
|
+
`assignee_json: ${JSON.stringify(JSON.stringify(transition.assignee))}`
|
|
13181
|
+
);
|
|
13182
|
+
}
|
|
13183
|
+
if (transition.requirements) {
|
|
13184
|
+
args.push(
|
|
13185
|
+
`requirements_json: ${JSON.stringify(JSON.stringify(transition.requirements))}`
|
|
13186
|
+
);
|
|
13187
|
+
}
|
|
13188
|
+
if (transition.permission) {
|
|
13189
|
+
args.push(
|
|
13190
|
+
`permission_json: ${JSON.stringify(JSON.stringify(transition.permission))}`
|
|
13191
|
+
);
|
|
13192
|
+
}
|
|
13193
|
+
if (transition.risk) {
|
|
13194
|
+
args.push(`risk: ${JSON.stringify(transition.risk)}`);
|
|
13195
|
+
}
|
|
13196
|
+
if (transition.expectedOutcome) {
|
|
13197
|
+
args.push(
|
|
13198
|
+
`expected_outcome_json: ${JSON.stringify(JSON.stringify(transition.expectedOutcome))}`
|
|
13199
|
+
);
|
|
13200
|
+
}
|
|
13201
|
+
return args.length > 0 ? `, ${args.join(", ")}` : "";
|
|
13202
|
+
}
|
|
12601
13203
|
function buildStateMachineModelMutations(modelPath, machines) {
|
|
12602
13204
|
const mutations = [];
|
|
12603
13205
|
for (const machine of machines || []) {
|
|
@@ -12608,12 +13210,13 @@ function buildStateMachineModelMutations(modelPath, machines) {
|
|
|
12608
13210
|
)}, entry_state: ${JSON.stringify(machine.entryState)}) { name } } }`
|
|
12609
13211
|
});
|
|
12610
13212
|
for (const state of normalizeStateDefinitions(machine)) {
|
|
12611
|
-
if (state.name === machine.entryState && !state.isFinal)
|
|
13213
|
+
if (state.name === machine.entryState && !state.isFinal && !state.label && !state.description)
|
|
13214
|
+
continue;
|
|
12612
13215
|
mutations.push({
|
|
12613
13216
|
label: `add state ${state.name} on ${modelPath}.${machine.name}`,
|
|
12614
13217
|
query: `mutation { at(path: ${JSON.stringify(modelPath)}) { state_machine(name: ${JSON.stringify(
|
|
12615
13218
|
machine.name
|
|
12616
|
-
)}) { add_state(name: ${JSON.stringify(state.name)}, is_final: ${state.isFinal}) { name } } } }`
|
|
13219
|
+
)}) { add_state(name: ${JSON.stringify(state.name)}, is_final: ${state.isFinal}, label: ${JSON.stringify(state.label || null)}, description: ${JSON.stringify(state.description || null)}) { name } } } }`
|
|
12617
13220
|
});
|
|
12618
13221
|
}
|
|
12619
13222
|
for (const transition of machine.transitions || []) {
|
|
@@ -12625,18 +13228,27 @@ function buildStateMachineModelMutations(modelPath, machines) {
|
|
|
12625
13228
|
transition.name
|
|
12626
13229
|
)}, from: ${JSON.stringify(transition.from)}, to: ${JSON.stringify(
|
|
12627
13230
|
transition.to
|
|
12628
|
-
)}) { name } } } }`
|
|
13231
|
+
)}${transitionMetadataGraphqlArgs(transition)}) { name } } } }`
|
|
12629
13232
|
});
|
|
12630
13233
|
}
|
|
12631
13234
|
}
|
|
12632
13235
|
return mutations;
|
|
12633
13236
|
}
|
|
12634
13237
|
function buildMachineTypes(classSummary, machine) {
|
|
13238
|
+
const stateGlossary = machine.states.map((state) => {
|
|
13239
|
+
const label = state.label && state.label !== state.name ? state.label : null;
|
|
13240
|
+
const meaning = [label, state.description].filter(Boolean).join(" \u2014 ");
|
|
13241
|
+
const finalMarker = state.isFinal ? " Final state." : "";
|
|
13242
|
+
return `${state.name}${meaning ? `: ${meaning}` : "."}${finalMarker}`;
|
|
13243
|
+
});
|
|
12635
13244
|
return [
|
|
12636
13245
|
{
|
|
12637
13246
|
kind: "union",
|
|
12638
13247
|
name: stateTypeName(classSummary.name, machine.name),
|
|
12639
|
-
docs: [
|
|
13248
|
+
docs: [
|
|
13249
|
+
`Allowed states for ${classSummary.name}.${machine.name}.`,
|
|
13250
|
+
...stateGlossary
|
|
13251
|
+
],
|
|
12640
13252
|
members: machine.states.map((state) => state.name)
|
|
12641
13253
|
},
|
|
12642
13254
|
{
|
|
@@ -12652,7 +13264,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
12652
13264
|
const transitionName = transitionTypeName(classSummary.name, machine.name);
|
|
12653
13265
|
pathTypeName(classSummary.name, machine.name);
|
|
12654
13266
|
const docsPrefix = `${classSummary.name}.${machine.name}`;
|
|
12655
|
-
|
|
13267
|
+
const methods = [
|
|
12656
13268
|
{
|
|
12657
13269
|
name: `get_${machine.name}`,
|
|
12658
13270
|
docs: [`Get the current ${docsPrefix} state.`],
|
|
@@ -12675,7 +13287,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
12675
13287
|
],
|
|
12676
13288
|
static: false,
|
|
12677
13289
|
params: [{ name: "target", type: stateName }],
|
|
12678
|
-
returnType: `Promise<${
|
|
13290
|
+
returnType: `Promise<${toPascalCase2(classSummary.name)}>`,
|
|
12679
13291
|
runtime: {
|
|
12680
13292
|
kind: "state_machine",
|
|
12681
13293
|
machineName: machine.name,
|
|
@@ -12748,6 +13360,99 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
12748
13360
|
}
|
|
12749
13361
|
}
|
|
12750
13362
|
];
|
|
13363
|
+
const creationMethods = (classSummary.methods || []).filter(
|
|
13364
|
+
(method) => method.static === true && Boolean(method.creates) && method.creates?.className === classSummary.name && typeof method.effectKey === "string" && method.effectKey.length > 0
|
|
13365
|
+
);
|
|
13366
|
+
for (const state of machine.states) {
|
|
13367
|
+
const stateNameValue = typeof state === "string" ? state : String(state?.name || "");
|
|
13368
|
+
if (!stateNameValue) continue;
|
|
13369
|
+
const token = methodToken(stateNameValue);
|
|
13370
|
+
methods.push(
|
|
13371
|
+
{
|
|
13372
|
+
name: `reach_${machine.name}_to_${token}`,
|
|
13373
|
+
docs: [`Reach ${docsPrefix} state ${stateNameValue}.`],
|
|
13374
|
+
static: false,
|
|
13375
|
+
params: [],
|
|
13376
|
+
returnType: `Promise<${toPascalCase2(classSummary.name)}>`,
|
|
13377
|
+
runtime: {
|
|
13378
|
+
kind: "state_machine",
|
|
13379
|
+
machineName: machine.name,
|
|
13380
|
+
className: classSummary.name,
|
|
13381
|
+
stateTypeName: stateName,
|
|
13382
|
+
transitionTypeName: transitionName,
|
|
13383
|
+
operation: "reach",
|
|
13384
|
+
targetState: stateNameValue,
|
|
13385
|
+
transitionActions: transitionActionsForMachine(machine)
|
|
13386
|
+
}
|
|
13387
|
+
},
|
|
13388
|
+
{
|
|
13389
|
+
name: `prepare_${machine.name}_to_${token}`,
|
|
13390
|
+
docs: [
|
|
13391
|
+
`Prepare a reviewable artifact that can move ${docsPrefix} to ${stateNameValue}.`
|
|
13392
|
+
],
|
|
13393
|
+
static: false,
|
|
13394
|
+
params: [],
|
|
13395
|
+
returnType: "Promise<SessionArtifactRecord>",
|
|
13396
|
+
runtime: {
|
|
13397
|
+
kind: "state_machine",
|
|
13398
|
+
machineName: machine.name,
|
|
13399
|
+
className: classSummary.name,
|
|
13400
|
+
stateTypeName: stateName,
|
|
13401
|
+
transitionTypeName: transitionName,
|
|
13402
|
+
operation: "prepare_reach",
|
|
13403
|
+
targetState: stateNameValue,
|
|
13404
|
+
transitionActions: transitionActionsForMachine(machine)
|
|
13405
|
+
}
|
|
13406
|
+
}
|
|
13407
|
+
);
|
|
13408
|
+
for (const creationMethod of creationMethods) {
|
|
13409
|
+
const creationRuntime = {
|
|
13410
|
+
kind: "state_machine",
|
|
13411
|
+
machineName: machine.name,
|
|
13412
|
+
className: classSummary.name,
|
|
13413
|
+
stateTypeName: stateName,
|
|
13414
|
+
transitionTypeName: transitionName,
|
|
13415
|
+
operation: "prepare_create_reach",
|
|
13416
|
+
targetState: stateNameValue,
|
|
13417
|
+
transitionActions: transitionActionsForMachine(machine),
|
|
13418
|
+
creation: {
|
|
13419
|
+
methodName: creationMethod.name,
|
|
13420
|
+
effectKey: creationMethod.effectKey || creationMethod.name,
|
|
13421
|
+
inputSchema: creationMethod.inputSchema,
|
|
13422
|
+
outputSchema: creationMethod.outputSchema,
|
|
13423
|
+
creates: creationMethod.creates
|
|
13424
|
+
}
|
|
13425
|
+
};
|
|
13426
|
+
const viaName = `prepare_${machine.name}_to_${token}_via_${methodToken(creationMethod.name)}`;
|
|
13427
|
+
methods.push({
|
|
13428
|
+
name: viaName,
|
|
13429
|
+
docs: [
|
|
13430
|
+
`Prepare a reviewable artifact that will create a new ${classSummary.name} through ${creationMethod.name}, then move ${docsPrefix} to ${stateNameValue}.`
|
|
13431
|
+
],
|
|
13432
|
+
static: true,
|
|
13433
|
+
params: [
|
|
13434
|
+
{ name: "input", type: "Record<string, any>", optional: true }
|
|
13435
|
+
],
|
|
13436
|
+
returnType: "Promise<SessionArtifactRecord>",
|
|
13437
|
+
runtime: creationRuntime
|
|
13438
|
+
});
|
|
13439
|
+
if (creationMethods.length === 1) {
|
|
13440
|
+
methods.push({
|
|
13441
|
+
name: `prepare_${machine.name}_to_${token}`,
|
|
13442
|
+
docs: [
|
|
13443
|
+
`Prepare a reviewable artifact that will create a new ${classSummary.name}, then move ${docsPrefix} to ${stateNameValue}.`
|
|
13444
|
+
],
|
|
13445
|
+
static: true,
|
|
13446
|
+
params: [
|
|
13447
|
+
{ name: "input", type: "Record<string, any>", optional: true }
|
|
13448
|
+
],
|
|
13449
|
+
returnType: "Promise<SessionArtifactRecord>",
|
|
13450
|
+
runtime: creationRuntime
|
|
13451
|
+
});
|
|
13452
|
+
}
|
|
13453
|
+
}
|
|
13454
|
+
}
|
|
13455
|
+
return methods;
|
|
12751
13456
|
}
|
|
12752
13457
|
function readStateMachineSummaries(rawClass) {
|
|
12753
13458
|
return {
|
|
@@ -12770,8 +13475,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12770
13475
|
type StateMachineMutation {
|
|
12771
13476
|
name: String!
|
|
12772
13477
|
state_machine: StateMachine!
|
|
12773
|
-
add_state(name: String!, is_final: Boolean): StateMachineMutation!
|
|
12774
|
-
add_transition(name: String!, from: String!, to: String
|
|
13478
|
+
add_state(name: String!, is_final: Boolean, label: String, description: String): StateMachineMutation!
|
|
13479
|
+
add_transition(name: String!, from: String!, to: String!, label: String, description: String, action_json: String, assignee_json: String, requirements_json: String, permission_json: String, risk: String, expected_outcome_json: String): StateMachineMutation!
|
|
12775
13480
|
activate_transition(name: String!): StateMachineMutation!
|
|
12776
13481
|
}
|
|
12777
13482
|
|
|
@@ -12788,6 +13493,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12788
13493
|
type StateMachineSnapshotMutation {
|
|
12789
13494
|
snapshot: StateMachineSnapshot!
|
|
12790
13495
|
activate_transition(name: String!): StateMachineSnapshotMutation!
|
|
13496
|
+
observe_state(state: String!, force: Boolean, source: String): StateMachineSnapshotMutation!
|
|
12791
13497
|
}
|
|
12792
13498
|
|
|
12793
13499
|
type StateMachine {
|
|
@@ -12807,6 +13513,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12807
13513
|
|
|
12808
13514
|
type StateMachineState {
|
|
12809
13515
|
name: String!
|
|
13516
|
+
label: String
|
|
13517
|
+
description: String
|
|
12810
13518
|
is_final: Boolean!
|
|
12811
13519
|
}
|
|
12812
13520
|
|
|
@@ -12814,6 +13522,14 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12814
13522
|
name: String!
|
|
12815
13523
|
from: StateMachineState!
|
|
12816
13524
|
to: StateMachineState!
|
|
13525
|
+
label: String
|
|
13526
|
+
description: String
|
|
13527
|
+
action_json: String
|
|
13528
|
+
assignee_json: String
|
|
13529
|
+
requirements_json: String
|
|
13530
|
+
permission_json: String
|
|
13531
|
+
risk: String
|
|
13532
|
+
expected_outcome_json: String
|
|
12817
13533
|
}
|
|
12818
13534
|
|
|
12819
13535
|
type StateMachinePath {
|
|
@@ -12866,23 +13582,47 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12866
13582
|
StateMachineMutation: {
|
|
12867
13583
|
name: (value) => value.name,
|
|
12868
13584
|
state_machine: async (value) => await run(value.target.state_machine(value.name)),
|
|
12869
|
-
add_state: async (value, { name, is_final }) => {
|
|
13585
|
+
add_state: async (value, { name, is_final, label, description }) => {
|
|
12870
13586
|
await run(
|
|
12871
13587
|
value.target.add_state_machine_state(
|
|
12872
13588
|
value.name,
|
|
12873
13589
|
name,
|
|
12874
|
-
is_final ?? false
|
|
13590
|
+
is_final ?? false,
|
|
13591
|
+
label,
|
|
13592
|
+
description
|
|
12875
13593
|
)
|
|
12876
13594
|
);
|
|
12877
13595
|
return value;
|
|
12878
13596
|
},
|
|
12879
|
-
add_transition: async (value, {
|
|
13597
|
+
add_transition: async (value, {
|
|
13598
|
+
name,
|
|
13599
|
+
from: from2,
|
|
13600
|
+
to,
|
|
13601
|
+
label,
|
|
13602
|
+
description,
|
|
13603
|
+
action_json,
|
|
13604
|
+
assignee_json,
|
|
13605
|
+
requirements_json,
|
|
13606
|
+
permission_json,
|
|
13607
|
+
risk,
|
|
13608
|
+
expected_outcome_json
|
|
13609
|
+
}) => {
|
|
12880
13610
|
await run(
|
|
12881
13611
|
value.target.add_state_machine_transition(
|
|
12882
13612
|
value.name,
|
|
12883
13613
|
name,
|
|
12884
|
-
|
|
12885
|
-
to
|
|
13614
|
+
from2,
|
|
13615
|
+
to,
|
|
13616
|
+
{
|
|
13617
|
+
label,
|
|
13618
|
+
description,
|
|
13619
|
+
actionJson: action_json,
|
|
13620
|
+
assigneeJson: assignee_json,
|
|
13621
|
+
requirementsJson: requirements_json,
|
|
13622
|
+
permissionJson: permission_json,
|
|
13623
|
+
risk,
|
|
13624
|
+
expectedOutcomeJson: expected_outcome_json
|
|
13625
|
+
}
|
|
12886
13626
|
)
|
|
12887
13627
|
);
|
|
12888
13628
|
return value;
|
|
@@ -12901,16 +13641,37 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12901
13641
|
value.target.activate_state_machine_transition(value.name, name)
|
|
12902
13642
|
);
|
|
12903
13643
|
return value;
|
|
13644
|
+
},
|
|
13645
|
+
observe_state: async (value, { state, force, source }) => {
|
|
13646
|
+
await run(
|
|
13647
|
+
value.target.observe_state_machine_state(
|
|
13648
|
+
value.name,
|
|
13649
|
+
state,
|
|
13650
|
+
force === true,
|
|
13651
|
+
source
|
|
13652
|
+
)
|
|
13653
|
+
);
|
|
13654
|
+
return value;
|
|
12904
13655
|
}
|
|
12905
13656
|
},
|
|
12906
13657
|
StateMachineState: {
|
|
12907
13658
|
name: (value) => value.name,
|
|
13659
|
+
label: (value) => value.label || null,
|
|
13660
|
+
description: (value) => value.description || null,
|
|
12908
13661
|
is_final: (value) => value.is_final
|
|
12909
13662
|
},
|
|
12910
13663
|
StateMachineTransition: {
|
|
12911
13664
|
name: (value) => value.name,
|
|
12912
13665
|
from: (value) => value.from_state || { name: value.from, is_final: false },
|
|
12913
|
-
to: (value) => value.to_state || { name: value.to, is_final: false }
|
|
13666
|
+
to: (value) => value.to_state || { name: value.to, is_final: false },
|
|
13667
|
+
label: (value) => value.label || null,
|
|
13668
|
+
description: (value) => value.description || null,
|
|
13669
|
+
action_json: (value) => value.action_json || null,
|
|
13670
|
+
assignee_json: (value) => value.assignee_json || null,
|
|
13671
|
+
requirements_json: (value) => value.requirements_json || null,
|
|
13672
|
+
permission_json: (value) => value.permission_json || null,
|
|
13673
|
+
risk: (value) => value.risk || null,
|
|
13674
|
+
expected_outcome_json: (value) => value.expected_outcome_json || null
|
|
12914
13675
|
},
|
|
12915
13676
|
StateMachinePath: {
|
|
12916
13677
|
states: (value) => value.states,
|
|
@@ -12977,6 +13738,14 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12977
13738
|
name
|
|
12978
13739
|
from { name }
|
|
12979
13740
|
to { name }
|
|
13741
|
+
label
|
|
13742
|
+
description
|
|
13743
|
+
action_json
|
|
13744
|
+
assignee_json
|
|
13745
|
+
requirements_json
|
|
13746
|
+
permission_json
|
|
13747
|
+
risk
|
|
13748
|
+
expected_outcome_json
|
|
12980
13749
|
}
|
|
12981
13750
|
}`
|
|
12982
13751
|
]
|
|
@@ -13194,6 +13963,18 @@ function buildEffectMetamodelMutations(toolPath, spec) {
|
|
|
13194
13963
|
}
|
|
13195
13964
|
|
|
13196
13965
|
// src/client.ts
|
|
13966
|
+
var DEFAULT_CONVERSATION_SESSION_LIST_LIMIT = 100;
|
|
13967
|
+
var MAX_CONVERSATION_SESSION_LIST_LIMIT = 500;
|
|
13968
|
+
var MAX_CONVERSATION_SESSION_LIST_OFFSET = 1e5;
|
|
13969
|
+
function boundedSessionListInteger(value, name, fallback, minimum, maximum) {
|
|
13970
|
+
if (value === void 0) return fallback;
|
|
13971
|
+
if (!Number.isInteger(value) || value < minimum || value > maximum) {
|
|
13972
|
+
throw new RangeError(
|
|
13973
|
+
`Session list ${name} must be an integer between ${minimum} and ${maximum}.`
|
|
13974
|
+
);
|
|
13975
|
+
}
|
|
13976
|
+
return value;
|
|
13977
|
+
}
|
|
13197
13978
|
var STANDARD_MODULES_OPERATIONS = [
|
|
13198
13979
|
{
|
|
13199
13980
|
create: "entity",
|
|
@@ -13230,6 +14011,26 @@ var STANDARD_MODULES_OPERATIONS = [
|
|
|
13230
14011
|
var BUILTIN_MODULES = {
|
|
13231
14012
|
standard_modules: STANDARD_MODULES_OPERATIONS
|
|
13232
14013
|
};
|
|
14014
|
+
function stateNameFromMethodName(methodName) {
|
|
14015
|
+
const raw = methodName.startsWith("to") ? methodName.slice(2) : methodName;
|
|
14016
|
+
return raw.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
14017
|
+
}
|
|
14018
|
+
function appendQueryOptions(searchParams, query) {
|
|
14019
|
+
for (const [key, value] of Object.entries(query || {})) {
|
|
14020
|
+
if (value === null || typeof value === "undefined" || value === "") {
|
|
14021
|
+
continue;
|
|
14022
|
+
}
|
|
14023
|
+
if (value instanceof Date) {
|
|
14024
|
+
searchParams.set(key, value.toISOString());
|
|
14025
|
+
continue;
|
|
14026
|
+
}
|
|
14027
|
+
if (Array.isArray(value)) {
|
|
14028
|
+
if (value.length > 0) searchParams.set(key, value.join(","));
|
|
14029
|
+
continue;
|
|
14030
|
+
}
|
|
14031
|
+
searchParams.set(key, String(value));
|
|
14032
|
+
}
|
|
14033
|
+
}
|
|
13233
14034
|
var DEFAULT_DIRECT_RECORD_OBJECTS_REQUEST_BATCH_SIZE = 100;
|
|
13234
14035
|
var MAX_RECORD_OBJECTS_CONCURRENCY = 16;
|
|
13235
14036
|
var DEFAULT_DIRECT_RECORD_OBJECTS_RETRY_COUNT = 3;
|
|
@@ -13260,8 +14061,20 @@ function bodyInitFromSessionFileUpload(body) {
|
|
|
13260
14061
|
return body;
|
|
13261
14062
|
}
|
|
13262
14063
|
var EFFECT_CATALOG_SYNC_TIMEOUT_MS = 12e4;
|
|
14064
|
+
var EFFECT_CATALOG_SYNC_BATCH_SIZE = 20;
|
|
13263
14065
|
var EFFECT_CATALOG_SYNC_RETRY_COUNT = 3;
|
|
13264
14066
|
var EFFECT_CATALOG_SYNC_RETRY_DELAY_MS = 1e3;
|
|
14067
|
+
function chunkItems(items, batchSize) {
|
|
14068
|
+
const chunks = [];
|
|
14069
|
+
for (let offset = 0; offset < items.length; offset += batchSize) {
|
|
14070
|
+
chunks.push(items.slice(offset, offset + batchSize));
|
|
14071
|
+
}
|
|
14072
|
+
return chunks;
|
|
14073
|
+
}
|
|
14074
|
+
function isUnsupportedEffectCatalogMutation(error) {
|
|
14075
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
14076
|
+
return message.includes("Unknown RPC method: effects.resetCatalog") || message.includes("Unknown RPC method: effects.addCatalog") || message.includes("Method not found: effects.resetCatalog") || message.includes("Method not found: effects.addCatalog");
|
|
14077
|
+
}
|
|
13265
14078
|
function planRecordObjectsChunks(records, batchSize) {
|
|
13266
14079
|
const total = records.length;
|
|
13267
14080
|
const size = Math.max(1, Math.min(batchSize, total));
|
|
@@ -13273,6 +14086,23 @@ function planRecordObjectsChunks(records, batchSize) {
|
|
|
13273
14086
|
}
|
|
13274
14087
|
return plans;
|
|
13275
14088
|
}
|
|
14089
|
+
function preserveRecordObjectRealId(record) {
|
|
14090
|
+
const realId = record.id.trim();
|
|
14091
|
+
if (!realId) {
|
|
14092
|
+
return record;
|
|
14093
|
+
}
|
|
14094
|
+
const fields = record.fields || {};
|
|
14095
|
+
if (typeof fields.real_id === "string" && fields.real_id.trim()) {
|
|
14096
|
+
return record;
|
|
14097
|
+
}
|
|
14098
|
+
return {
|
|
14099
|
+
...record,
|
|
14100
|
+
fields: {
|
|
14101
|
+
...fields,
|
|
14102
|
+
real_id: realId
|
|
14103
|
+
}
|
|
14104
|
+
};
|
|
14105
|
+
}
|
|
13276
14106
|
function computeEffectKey2(effect) {
|
|
13277
14107
|
const attachedClass = effect.className?.trim();
|
|
13278
14108
|
if (!attachedClass) {
|
|
@@ -13483,7 +14313,7 @@ var Environment = class _Environment {
|
|
|
13483
14313
|
}
|
|
13484
14314
|
get sessions() {
|
|
13485
14315
|
return {
|
|
13486
|
-
list: async (options) => this.listSessions(options
|
|
14316
|
+
list: async (options = {}) => this.listSessions(options),
|
|
13487
14317
|
create: async (options) => this.createSession(options),
|
|
13488
14318
|
connect: async (sessionId, options) => this.connectSession(sessionId, options),
|
|
13489
14319
|
reopen: async (sessionId, options) => this.reopenSession(sessionId, options),
|
|
@@ -13510,11 +14340,105 @@ var Environment = class _Environment {
|
|
|
13510
14340
|
getAwaitingCount: async () => this.getAwaitingRecordCount()
|
|
13511
14341
|
};
|
|
13512
14342
|
}
|
|
14343
|
+
/**
|
|
14344
|
+
* Mirror product-owned workflow state into Granular without making Granular
|
|
14345
|
+
* own the customer application's state machine.
|
|
14346
|
+
*/
|
|
14347
|
+
async recordState(input) {
|
|
14348
|
+
const { machine, state, ...target } = input;
|
|
14349
|
+
if (!machine.trim()) {
|
|
14350
|
+
throw new Error("State update requires a machine name");
|
|
14351
|
+
}
|
|
14352
|
+
if (!state.trim()) {
|
|
14353
|
+
throw new Error("State update requires a state");
|
|
14354
|
+
}
|
|
14355
|
+
return this.recordObject({
|
|
14356
|
+
className: target.className,
|
|
14357
|
+
id: target.id,
|
|
14358
|
+
...target.label ? { label: target.label } : {},
|
|
14359
|
+
...target.fields ? { fields: target.fields } : {},
|
|
14360
|
+
...target.relationships ? { relationships: target.relationships } : {},
|
|
14361
|
+
states: {
|
|
14362
|
+
[machine.trim()]: {
|
|
14363
|
+
state: state.trim(),
|
|
14364
|
+
...target.source ? { source: target.source } : {},
|
|
14365
|
+
...target.cause ? { cause: target.cause } : {},
|
|
14366
|
+
...target.actorId ? { actorId: target.actorId } : {},
|
|
14367
|
+
...target.observedAt !== void 0 ? { observedAt: target.observedAt } : {},
|
|
14368
|
+
...target.force !== void 0 ? { force: target.force } : {},
|
|
14369
|
+
...target.metadata ? { metadata: target.metadata } : {}
|
|
14370
|
+
}
|
|
14371
|
+
}
|
|
14372
|
+
});
|
|
14373
|
+
}
|
|
14374
|
+
/**
|
|
14375
|
+
* Mirror product-owned workflow state into Granular without making Granular
|
|
14376
|
+
* own the customer application's state machine.
|
|
14377
|
+
*
|
|
14378
|
+
* Example:
|
|
14379
|
+
* `await env.recordState({ className: "spend_request", id, machine: "lifecycle", state: "policy_review", source: "customer_backend" })`
|
|
14380
|
+
*/
|
|
14381
|
+
state(target) {
|
|
14382
|
+
const observe = async (machineName, stateName, input = {}) => {
|
|
14383
|
+
const observedState = input.observedState || input.state || stateName;
|
|
14384
|
+
if (!observedState) {
|
|
14385
|
+
throw new Error("State observation requires a target state");
|
|
14386
|
+
}
|
|
14387
|
+
return this.recordState({
|
|
14388
|
+
...target,
|
|
14389
|
+
machine: machineName,
|
|
14390
|
+
state: observedState,
|
|
14391
|
+
...input.source ? { source: input.source } : {},
|
|
14392
|
+
...input.cause ? { cause: input.cause } : {},
|
|
14393
|
+
...input.actorId ? { actorId: input.actorId } : {},
|
|
14394
|
+
...input.observedAt !== void 0 ? { observedAt: input.observedAt } : {},
|
|
14395
|
+
...input.force !== void 0 ? { force: input.force } : {},
|
|
14396
|
+
...input.metadata ? { metadata: input.metadata } : {}
|
|
14397
|
+
});
|
|
14398
|
+
};
|
|
14399
|
+
return new Proxy(
|
|
14400
|
+
{},
|
|
14401
|
+
{
|
|
14402
|
+
get: (_target, machineProperty) => {
|
|
14403
|
+
if (typeof machineProperty !== "string") return void 0;
|
|
14404
|
+
return new Proxy(
|
|
14405
|
+
{},
|
|
14406
|
+
{
|
|
14407
|
+
get: (_machineTarget, stateProperty) => {
|
|
14408
|
+
if (stateProperty === "to") {
|
|
14409
|
+
return (stateName, input) => observe(machineProperty, stateName, input || {});
|
|
14410
|
+
}
|
|
14411
|
+
if (typeof stateProperty !== "string") return void 0;
|
|
14412
|
+
return (input) => observe(
|
|
14413
|
+
machineProperty,
|
|
14414
|
+
stateNameFromMethodName(stateProperty),
|
|
14415
|
+
input || {}
|
|
14416
|
+
);
|
|
14417
|
+
}
|
|
14418
|
+
}
|
|
14419
|
+
);
|
|
14420
|
+
}
|
|
14421
|
+
}
|
|
14422
|
+
);
|
|
14423
|
+
}
|
|
13513
14424
|
get feedback() {
|
|
13514
14425
|
return {
|
|
13515
14426
|
list: async () => this.listFeedback()
|
|
13516
14427
|
};
|
|
13517
14428
|
}
|
|
14429
|
+
get manualActions() {
|
|
14430
|
+
return {
|
|
14431
|
+
record: (input) => this.recordManualAction(input),
|
|
14432
|
+
list: (options = {}) => this.listManualActions(options),
|
|
14433
|
+
suggest: (options = {}) => this.suggestManualActions(options)
|
|
14434
|
+
};
|
|
14435
|
+
}
|
|
14436
|
+
get artifactApprovals() {
|
|
14437
|
+
return {
|
|
14438
|
+
list: (options = {}) => this.listArtifactApprovals(options),
|
|
14439
|
+
decide: (approvalTaskId, input) => this.decideArtifactApproval(approvalTaskId, input)
|
|
14440
|
+
};
|
|
14441
|
+
}
|
|
13518
14442
|
/**
|
|
13519
14443
|
* Sessionless environments do not own a live transport, so disconnecting the
|
|
13520
14444
|
* environment handle itself is a no-op. This keeps the public surface
|
|
@@ -13524,17 +14448,12 @@ var Environment = class _Environment {
|
|
|
13524
14448
|
*/
|
|
13525
14449
|
async disconnect() {
|
|
13526
14450
|
}
|
|
13527
|
-
async listSessions(
|
|
13528
|
-
|
|
13529
|
-
|
|
13530
|
-
|
|
13531
|
-
|
|
13532
|
-
|
|
13533
|
-
return [...active, ...closed].sort(
|
|
13534
|
-
(left, right) => Date.parse(right.lastSeenAt) - Date.parse(left.lastSeenAt)
|
|
13535
|
-
);
|
|
13536
|
-
}
|
|
13537
|
-
return status === "closed" ? this.granular.listClosedSessions({ environmentId: this.environmentId }) : this.granular.listOpenSessions({ environmentId: this.environmentId });
|
|
14451
|
+
async listSessions(optionsOrStatus = {}) {
|
|
14452
|
+
const options = typeof optionsOrStatus === "string" ? { status: optionsOrStatus } : optionsOrStatus;
|
|
14453
|
+
return this.granular.listSessions({
|
|
14454
|
+
...options,
|
|
14455
|
+
environmentId: this.environmentId
|
|
14456
|
+
});
|
|
13538
14457
|
}
|
|
13539
14458
|
async getUserEnvironmentState(options = {}) {
|
|
13540
14459
|
return this.granular.getUserEnvironmentState({
|
|
@@ -13552,6 +14471,7 @@ var Environment = class _Environment {
|
|
|
13552
14471
|
return this.granular.createSession({
|
|
13553
14472
|
environmentId: this.environmentId,
|
|
13554
14473
|
clientId: options?.clientId,
|
|
14474
|
+
sessionScope: options?.sessionScope,
|
|
13555
14475
|
initialHeap: options?.initialHeap
|
|
13556
14476
|
});
|
|
13557
14477
|
}
|
|
@@ -13593,6 +14513,50 @@ var Environment = class _Environment {
|
|
|
13593
14513
|
const response = await this.controlPlaneRequest(`/control/environments/${this.environmentId}/feedback`);
|
|
13594
14514
|
return Array.isArray(response.items) ? response.items : [];
|
|
13595
14515
|
}
|
|
14516
|
+
async recordManualAction(input) {
|
|
14517
|
+
const body = {
|
|
14518
|
+
...input,
|
|
14519
|
+
...input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {}
|
|
14520
|
+
};
|
|
14521
|
+
return this.controlPlaneRequest(
|
|
14522
|
+
`/control/environments/${this.environmentId}/manual-actions`,
|
|
14523
|
+
{
|
|
14524
|
+
method: "POST",
|
|
14525
|
+
body: JSON.stringify(body)
|
|
14526
|
+
}
|
|
14527
|
+
);
|
|
14528
|
+
}
|
|
14529
|
+
async listManualActions(options = {}) {
|
|
14530
|
+
const query = new URLSearchParams();
|
|
14531
|
+
appendQueryOptions(query, options);
|
|
14532
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
14533
|
+
return this.controlPlaneRequest(`/control/environments/${this.environmentId}/manual-actions${suffix}`);
|
|
14534
|
+
}
|
|
14535
|
+
async suggestManualActions(options = {}) {
|
|
14536
|
+
const query = new URLSearchParams();
|
|
14537
|
+
appendQueryOptions(query, options);
|
|
14538
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
14539
|
+
return this.controlPlaneRequest(
|
|
14540
|
+
`/control/environments/${this.environmentId}/manual-actions/suggestions${suffix}`
|
|
14541
|
+
);
|
|
14542
|
+
}
|
|
14543
|
+
async listArtifactApprovals(options = {}) {
|
|
14544
|
+
const query = new URLSearchParams();
|
|
14545
|
+
appendQueryOptions(query, options);
|
|
14546
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
14547
|
+
return this.controlPlaneRequest(
|
|
14548
|
+
`/control/environments/${this.environmentId}/artifact-approvals${suffix}`
|
|
14549
|
+
);
|
|
14550
|
+
}
|
|
14551
|
+
async decideArtifactApproval(approvalTaskId, input) {
|
|
14552
|
+
return this.controlPlaneRequest(
|
|
14553
|
+
`/control/environments/${this.environmentId}/artifact-approvals/${encodeURIComponent(approvalTaskId)}/decide`,
|
|
14554
|
+
{
|
|
14555
|
+
method: "POST",
|
|
14556
|
+
body: JSON.stringify(input)
|
|
14557
|
+
}
|
|
14558
|
+
);
|
|
14559
|
+
}
|
|
13596
14560
|
getRuntimeBaseUrl() {
|
|
13597
14561
|
return deriveRuntimeBaseUrl(this._apiEndpoint);
|
|
13598
14562
|
}
|
|
@@ -14417,10 +15381,11 @@ var Environment = class _Environment {
|
|
|
14417
15381
|
if (!Array.isArray(records) || records.length === 0) {
|
|
14418
15382
|
return [];
|
|
14419
15383
|
}
|
|
15384
|
+
const recordsToWrite = records.map(preserveRecordObjectRealId);
|
|
14420
15385
|
const batchSize = Math.max(
|
|
14421
15386
|
1,
|
|
14422
15387
|
Math.min(
|
|
14423
|
-
|
|
15388
|
+
recordsToWrite.length,
|
|
14424
15389
|
options?.batchSize ?? DEFAULT_DIRECT_RECORD_OBJECTS_REQUEST_BATCH_SIZE
|
|
14425
15390
|
)
|
|
14426
15391
|
);
|
|
@@ -14428,8 +15393,8 @@ var Environment = class _Environment {
|
|
|
14428
15393
|
MAX_RECORD_OBJECTS_CONCURRENCY,
|
|
14429
15394
|
Math.max(1, options?.concurrency ?? 1)
|
|
14430
15395
|
);
|
|
14431
|
-
const plans = planRecordObjectsChunks(
|
|
14432
|
-
const total =
|
|
15396
|
+
const plans = planRecordObjectsChunks(recordsToWrite, batchSize);
|
|
15397
|
+
const total = recordsToWrite.length;
|
|
14433
15398
|
const results = new Array(total);
|
|
14434
15399
|
const onChunk = options?.onChunkComplete;
|
|
14435
15400
|
for (let waveStart = 0; waveStart < plans.length; waveStart += concurrency) {
|
|
@@ -14500,12 +15465,13 @@ var Environment = class _Environment {
|
|
|
14500
15465
|
* synchronous upserts and fine-grained chunk progress via **`onChunkComplete`**.
|
|
14501
15466
|
*/
|
|
14502
15467
|
async enqueueRecordImport(records, options = {}) {
|
|
15468
|
+
const recordsToImport = records.map(preserveRecordObjectRealId);
|
|
14503
15469
|
return this.controlPlaneRequest(
|
|
14504
15470
|
`/control/environments/${this.environmentId}/record-imports`,
|
|
14505
15471
|
{
|
|
14506
15472
|
method: "POST",
|
|
14507
15473
|
body: JSON.stringify({
|
|
14508
|
-
records,
|
|
15474
|
+
records: recordsToImport,
|
|
14509
15475
|
batchSize: options.batchSize,
|
|
14510
15476
|
setupRunId: options.setupRunId,
|
|
14511
15477
|
writeMode: options.writeMode
|
|
@@ -14613,11 +15579,7 @@ var EnvironmentSession = class extends Session {
|
|
|
14613
15579
|
}
|
|
14614
15580
|
buildSessionDataUrl(path2, query) {
|
|
14615
15581
|
const searchParams = new URLSearchParams();
|
|
14616
|
-
|
|
14617
|
-
if (value !== null && typeof value !== "undefined" && value !== "") {
|
|
14618
|
-
searchParams.set(key, String(value));
|
|
14619
|
-
}
|
|
14620
|
-
}
|
|
15582
|
+
appendQueryOptions(searchParams, query);
|
|
14621
15583
|
const queryString = searchParams.toString();
|
|
14622
15584
|
return `${this.environment.runtimeBaseUrl}${this.sessionDataRoutePrefix}/${encodeURIComponent(this.sessionId)}${path2}${queryString ? `?${queryString}` : ""}`;
|
|
14623
15585
|
}
|
|
@@ -14700,9 +15662,108 @@ var EnvironmentSession = class extends Session {
|
|
|
14700
15662
|
),
|
|
14701
15663
|
get: (jobId) => this.sessionDataRequest(
|
|
14702
15664
|
`/jobs/${encodeURIComponent(jobId)}`
|
|
15665
|
+
),
|
|
15666
|
+
latest: async (options = {}) => {
|
|
15667
|
+
const page = await this.sessionDataRequest("/jobs", {
|
|
15668
|
+
status: options.status || "all",
|
|
15669
|
+
latest: true,
|
|
15670
|
+
limit: 1
|
|
15671
|
+
});
|
|
15672
|
+
return page.items[0] || null;
|
|
15673
|
+
}
|
|
15674
|
+
};
|
|
15675
|
+
}
|
|
15676
|
+
get artifacts() {
|
|
15677
|
+
return {
|
|
15678
|
+
list: (options = {}) => {
|
|
15679
|
+
const queryOptions = { ...options };
|
|
15680
|
+
if (options.target) {
|
|
15681
|
+
queryOptions.targetClassName = options.target.className;
|
|
15682
|
+
queryOptions.targetId = options.target.id;
|
|
15683
|
+
delete queryOptions.target;
|
|
15684
|
+
}
|
|
15685
|
+
return this.sessionDataRequest("/artifacts", queryOptions);
|
|
15686
|
+
},
|
|
15687
|
+
listForLatestJob: (options = {}) => this.artifacts.list({
|
|
15688
|
+
...options,
|
|
15689
|
+
latestJob: true
|
|
15690
|
+
}),
|
|
15691
|
+
get: (artifactId) => this.sessionDataRequest(
|
|
15692
|
+
`/artifacts/${encodeURIComponent(artifactId)}`
|
|
15693
|
+
),
|
|
15694
|
+
create: (artifact) => this.sessionDataRequest(
|
|
15695
|
+
"/artifacts",
|
|
15696
|
+
void 0,
|
|
15697
|
+
{
|
|
15698
|
+
method: "POST",
|
|
15699
|
+
body: artifact
|
|
15700
|
+
}
|
|
15701
|
+
),
|
|
15702
|
+
updateInputs: (artifactId, patch) => this.sessionDataRequest(
|
|
15703
|
+
`/artifacts/${encodeURIComponent(artifactId)}`,
|
|
15704
|
+
void 0,
|
|
15705
|
+
{
|
|
15706
|
+
method: "PATCH",
|
|
15707
|
+
body: patch
|
|
15708
|
+
}
|
|
15709
|
+
),
|
|
15710
|
+
validate: (artifactId) => this.sessionDataRequest(
|
|
15711
|
+
`/artifacts/${encodeURIComponent(artifactId)}/validate`,
|
|
15712
|
+
void 0,
|
|
15713
|
+
{ method: "POST" }
|
|
15714
|
+
),
|
|
15715
|
+
execute: (artifactId, options) => this.sessionDataRequest(
|
|
15716
|
+
`/artifacts/${encodeURIComponent(artifactId)}/execute`,
|
|
15717
|
+
void 0,
|
|
15718
|
+
{ method: "POST", body: options }
|
|
15719
|
+
),
|
|
15720
|
+
approve: (artifactId, options) => this.sessionDataRequest(
|
|
15721
|
+
`/artifacts/${encodeURIComponent(artifactId)}/approve`,
|
|
15722
|
+
void 0,
|
|
15723
|
+
{ method: "POST", body: options }
|
|
15724
|
+
),
|
|
15725
|
+
cancel: (artifactId) => this.sessionDataRequest(
|
|
15726
|
+
`/artifacts/${encodeURIComponent(artifactId)}/cancel`,
|
|
15727
|
+
void 0,
|
|
15728
|
+
{ method: "POST" }
|
|
14703
15729
|
)
|
|
14704
15730
|
};
|
|
14705
15731
|
}
|
|
15732
|
+
get manualActions() {
|
|
15733
|
+
const useDelegatedBrowserRoute = this.sessionDataRoutePrefix === "/sdk/browser-sessions";
|
|
15734
|
+
return {
|
|
15735
|
+
record: (input) => useDelegatedBrowserRoute ? this.sessionDataRequest(
|
|
15736
|
+
"/manual-actions",
|
|
15737
|
+
void 0,
|
|
15738
|
+
{
|
|
15739
|
+
method: "POST",
|
|
15740
|
+
body: { ...input, sessionId: this.sessionId }
|
|
15741
|
+
}
|
|
15742
|
+
) : this.environment.manualActions.record({
|
|
15743
|
+
...input,
|
|
15744
|
+
sessionId: this.sessionId
|
|
15745
|
+
}),
|
|
15746
|
+
list: (options = {}) => useDelegatedBrowserRoute ? this.sessionDataRequest("/manual-actions", { ...options, sessionId: this.sessionId }) : this.environment.manualActions.list({
|
|
15747
|
+
...options,
|
|
15748
|
+
sessionId: this.sessionId
|
|
15749
|
+
}),
|
|
15750
|
+
suggest: (options = {}) => useDelegatedBrowserRoute ? this.sessionDataRequest(
|
|
15751
|
+
"/manual-actions/suggestions",
|
|
15752
|
+
options
|
|
15753
|
+
) : this.environment.manualActions.suggest(options)
|
|
15754
|
+
};
|
|
15755
|
+
}
|
|
15756
|
+
get artifactApprovals() {
|
|
15757
|
+
const useDelegatedBrowserRoute = this.sessionDataRoutePrefix === "/sdk/browser-sessions";
|
|
15758
|
+
return {
|
|
15759
|
+
list: (options = {}) => useDelegatedBrowserRoute ? this.sessionDataRequest("/artifact-approvals", options) : this.environment.artifactApprovals.list(options),
|
|
15760
|
+
decide: (approvalTaskId, input) => useDelegatedBrowserRoute ? this.sessionDataRequest(
|
|
15761
|
+
`/artifact-approvals/${encodeURIComponent(approvalTaskId)}/decide`,
|
|
15762
|
+
void 0,
|
|
15763
|
+
{ method: "POST", body: input }
|
|
15764
|
+
) : this.environment.artifactApprovals.decide(approvalTaskId, input)
|
|
15765
|
+
};
|
|
15766
|
+
}
|
|
14706
15767
|
get files() {
|
|
14707
15768
|
return {
|
|
14708
15769
|
list: (options = {}) => this.sessionDataRequest(
|
|
@@ -14783,13 +15844,16 @@ var EnvironmentSession = class extends Session {
|
|
|
14783
15844
|
get transcript() {
|
|
14784
15845
|
return {
|
|
14785
15846
|
list: async (options = {}) => {
|
|
14786
|
-
const [messages, jobs, entries, lists] = await Promise.all([
|
|
15847
|
+
const [messages, jobs, entries, lists, artifacts] = await Promise.all([
|
|
14787
15848
|
this.collectAllSessionItems(this.messages.list),
|
|
14788
15849
|
this.collectAllSessionItems(
|
|
14789
15850
|
(pageOptions) => this.jobs.list({ ...pageOptions, status: "all" })
|
|
14790
15851
|
),
|
|
14791
15852
|
this.collectAllSessionItems(this.heap.entries.list),
|
|
14792
|
-
this.collectAllSessionItems(this.heap.lists.list)
|
|
15853
|
+
this.collectAllSessionItems(this.heap.lists.list),
|
|
15854
|
+
this.collectAllSessionItems(
|
|
15855
|
+
(pageOptions) => this.artifacts.list({ ...pageOptions, status: "all" })
|
|
15856
|
+
)
|
|
14793
15857
|
]);
|
|
14794
15858
|
const liveDoc = {
|
|
14795
15859
|
conversation: { messages },
|
|
@@ -14803,6 +15867,21 @@ var EnvironmentSession = class extends Session {
|
|
|
14803
15867
|
(entry) => Boolean(entry)
|
|
14804
15868
|
)
|
|
14805
15869
|
)
|
|
15870
|
+
},
|
|
15871
|
+
artifacts: {
|
|
15872
|
+
byId: Object.fromEntries(
|
|
15873
|
+
artifacts.map((artifact) => {
|
|
15874
|
+
return artifact?.artifactId ? [
|
|
15875
|
+
artifact.artifactId,
|
|
15876
|
+
artifact
|
|
15877
|
+
] : null;
|
|
15878
|
+
}).filter(
|
|
15879
|
+
(entry) => Boolean(entry)
|
|
15880
|
+
)
|
|
15881
|
+
),
|
|
15882
|
+
order: artifacts.map((artifact) => artifact?.artifactId).filter(
|
|
15883
|
+
(artifactId) => Boolean(artifactId)
|
|
15884
|
+
)
|
|
14806
15885
|
}
|
|
14807
15886
|
};
|
|
14808
15887
|
const heap = normalizeHeapSnapshot({
|
|
@@ -14879,6 +15958,12 @@ var EnvironmentSession = class extends Session {
|
|
|
14879
15958
|
async recordObject(options) {
|
|
14880
15959
|
return this.environment.recordObject(options);
|
|
14881
15960
|
}
|
|
15961
|
+
async recordState(input) {
|
|
15962
|
+
return this.environment.recordState(input);
|
|
15963
|
+
}
|
|
15964
|
+
state(target) {
|
|
15965
|
+
return this.environment.state(target);
|
|
15966
|
+
}
|
|
14882
15967
|
async recordObjects(records, options) {
|
|
14883
15968
|
return this.environment.recordObjects(records, options);
|
|
14884
15969
|
}
|
|
@@ -14949,7 +16034,7 @@ var EnvironmentSession = class extends Session {
|
|
|
14949
16034
|
* Close only the socket transport without sending `client.goodbye`.
|
|
14950
16035
|
*/
|
|
14951
16036
|
disconnectTransport() {
|
|
14952
|
-
this.client.disconnect();
|
|
16037
|
+
this.client.disconnect({ reason: "Transport detach" });
|
|
14953
16038
|
}
|
|
14954
16039
|
/**
|
|
14955
16040
|
* Backwards-compatible alias for `disconnect()`.
|
|
@@ -15344,16 +16429,71 @@ var Granular = class _Granular {
|
|
|
15344
16429
|
};
|
|
15345
16430
|
}
|
|
15346
16431
|
/**
|
|
15347
|
-
* List
|
|
16432
|
+
* List indexed sessions using ownership filters and bounded pagination.
|
|
16433
|
+
*/
|
|
16434
|
+
async listSessions(options) {
|
|
16435
|
+
const environmentId = options.environmentId?.trim();
|
|
16436
|
+
const sandboxId = options.sandboxId?.trim();
|
|
16437
|
+
const subjectId = options.subjectId?.trim();
|
|
16438
|
+
if (!environmentId && !sandboxId && !subjectId) {
|
|
16439
|
+
throw new Error(
|
|
16440
|
+
"listSessions() requires environmentId, sandboxId, or subjectId so history cannot be scanned accidentally."
|
|
16441
|
+
);
|
|
16442
|
+
}
|
|
16443
|
+
const status = options.status || "active";
|
|
16444
|
+
const allowedStatuses = /* @__PURE__ */ new Set([
|
|
16445
|
+
"active",
|
|
16446
|
+
"closed",
|
|
16447
|
+
"expired",
|
|
16448
|
+
"failed",
|
|
16449
|
+
"timeout",
|
|
16450
|
+
"all"
|
|
16451
|
+
]);
|
|
16452
|
+
if (!allowedStatuses.has(status)) {
|
|
16453
|
+
throw new Error(`Unsupported session status: ${String(status)}`);
|
|
16454
|
+
}
|
|
16455
|
+
const limit = boundedSessionListInteger(
|
|
16456
|
+
options.limit,
|
|
16457
|
+
"limit",
|
|
16458
|
+
DEFAULT_CONVERSATION_SESSION_LIST_LIMIT,
|
|
16459
|
+
1,
|
|
16460
|
+
MAX_CONVERSATION_SESSION_LIST_LIMIT
|
|
16461
|
+
);
|
|
16462
|
+
const offset = boundedSessionListInteger(
|
|
16463
|
+
options.offset,
|
|
16464
|
+
"offset",
|
|
16465
|
+
0,
|
|
16466
|
+
0,
|
|
16467
|
+
MAX_CONVERSATION_SESSION_LIST_OFFSET
|
|
16468
|
+
);
|
|
16469
|
+
const query = new URLSearchParams({
|
|
16470
|
+
limit: String(limit),
|
|
16471
|
+
offset: String(offset)
|
|
16472
|
+
});
|
|
16473
|
+
if (environmentId) query.set("environmentId", environmentId);
|
|
16474
|
+
if (sandboxId) query.set("sandboxId", sandboxId);
|
|
16475
|
+
if (subjectId) query.set("userId", subjectId);
|
|
16476
|
+
if (options.sessionScope?.trim()) {
|
|
16477
|
+
query.set("sessionScope", options.sessionScope.trim());
|
|
16478
|
+
}
|
|
16479
|
+
if (status !== "all") query.set("status", status);
|
|
16480
|
+
const res = await this.request(
|
|
16481
|
+
`/control/sessions?${query.toString()}`
|
|
16482
|
+
);
|
|
16483
|
+
const items = Array.isArray(res.items) ? res.items : [];
|
|
16484
|
+
return items.map((row) => this.normalizeConversationSession(row));
|
|
16485
|
+
}
|
|
16486
|
+
/**
|
|
16487
|
+
* List active (open) sessions for an environment.
|
|
15348
16488
|
*/
|
|
15349
16489
|
async listOpenSessions(filters) {
|
|
15350
|
-
return this.
|
|
16490
|
+
return this.listSessions({ ...filters, status: "active" });
|
|
15351
16491
|
}
|
|
15352
16492
|
/**
|
|
15353
16493
|
* List closed sessions for an environment (conversations that have disconnected).
|
|
15354
16494
|
*/
|
|
15355
16495
|
async listClosedSessions(filters) {
|
|
15356
|
-
return this.
|
|
16496
|
+
return this.listSessions({ ...filters, status: "closed" });
|
|
15357
16497
|
}
|
|
15358
16498
|
async getUserEnvironmentState(options) {
|
|
15359
16499
|
const query = new URLSearchParams({
|
|
@@ -15388,14 +16528,6 @@ var Granular = class _Granular {
|
|
|
15388
16528
|
});
|
|
15389
16529
|
return result.readAtBySessionId || {};
|
|
15390
16530
|
}
|
|
15391
|
-
async listSessionsForEnvironment(environmentId, status) {
|
|
15392
|
-
const query = new URLSearchParams({ environmentId, status });
|
|
15393
|
-
const res = await this.request(
|
|
15394
|
-
`/control/sessions?${query.toString()}`
|
|
15395
|
-
);
|
|
15396
|
-
const items = Array.isArray(res.items) ? res.items : [];
|
|
15397
|
-
return items.map((row) => this.normalizeConversationSession(row));
|
|
15398
|
-
}
|
|
15399
16531
|
normalizeConversationSession(row) {
|
|
15400
16532
|
const sessionId = String(row.sessionId ?? row.session_id ?? "");
|
|
15401
16533
|
const environmentId = String(row.environmentId ?? row.environment_id ?? "");
|
|
@@ -15454,6 +16586,7 @@ var Granular = class _Granular {
|
|
|
15454
16586
|
*/
|
|
15455
16587
|
async createSession(options) {
|
|
15456
16588
|
const clientId = options.clientId || `client_${Date.now()}`;
|
|
16589
|
+
const sessionScope = options.sessionScope?.trim() || void 0;
|
|
15457
16590
|
await this.activateEnvironment(options.environmentId);
|
|
15458
16591
|
const envData = await this.environments.get(options.environmentId);
|
|
15459
16592
|
const environment = this.bindEnvironmentHandle(envData);
|
|
@@ -15462,6 +16595,8 @@ var Granular = class _Granular {
|
|
|
15462
16595
|
body: JSON.stringify({
|
|
15463
16596
|
environmentId: options.environmentId,
|
|
15464
16597
|
clientId,
|
|
16598
|
+
sessionScope,
|
|
16599
|
+
capabilities: sessionScope ? { sessionScope } : void 0,
|
|
15465
16600
|
initialHeap: options.initialHeap
|
|
15466
16601
|
})
|
|
15467
16602
|
});
|
|
@@ -15706,15 +16841,43 @@ var Granular = class _Granular {
|
|
|
15706
16841
|
const effects = Array.from(
|
|
15707
16842
|
this.getSandboxEffectMap(host.sandboxId).values()
|
|
15708
16843
|
).map((effect) => this.serializeEffect(effect));
|
|
15709
|
-
|
|
15710
|
-
|
|
15711
|
-
|
|
15712
|
-
|
|
15713
|
-
|
|
15714
|
-
|
|
15715
|
-
|
|
15716
|
-
|
|
15717
|
-
|
|
16844
|
+
let acceptedCount = 0;
|
|
16845
|
+
const rejected = [];
|
|
16846
|
+
try {
|
|
16847
|
+
await withTimeout(
|
|
16848
|
+
host.wsClient.call("effects.resetCatalog", {}),
|
|
16849
|
+
EFFECT_CATALOG_SYNC_TIMEOUT_MS,
|
|
16850
|
+
`effects.resetCatalog for sandbox ${host.sandboxId}`
|
|
16851
|
+
);
|
|
16852
|
+
for (const batch of chunkItems(effects, EFFECT_CATALOG_SYNC_BATCH_SIZE)) {
|
|
16853
|
+
const result = await withTimeout(
|
|
16854
|
+
host.wsClient.call("effects.addCatalog", {
|
|
16855
|
+
effects: batch
|
|
16856
|
+
}),
|
|
16857
|
+
EFFECT_CATALOG_SYNC_TIMEOUT_MS,
|
|
16858
|
+
`effects.addCatalog for sandbox ${host.sandboxId}`
|
|
16859
|
+
);
|
|
16860
|
+
acceptedCount += typeof result?.acceptedCount === "number" ? result.acceptedCount : 0;
|
|
16861
|
+
if (Array.isArray(result?.rejected)) {
|
|
16862
|
+
rejected.push(...result.rejected);
|
|
16863
|
+
}
|
|
16864
|
+
}
|
|
16865
|
+
} catch (error) {
|
|
16866
|
+
if (!isUnsupportedEffectCatalogMutation(error)) {
|
|
16867
|
+
throw error;
|
|
16868
|
+
}
|
|
16869
|
+
const result = await withTimeout(
|
|
16870
|
+
host.wsClient.call("effects.publishCatalog", {
|
|
16871
|
+
effects
|
|
16872
|
+
}),
|
|
16873
|
+
EFFECT_CATALOG_SYNC_TIMEOUT_MS,
|
|
16874
|
+
`effects.publishCatalog for sandbox ${host.sandboxId}`
|
|
16875
|
+
);
|
|
16876
|
+
acceptedCount = typeof result?.acceptedCount === "number" ? result.acceptedCount : 0;
|
|
16877
|
+
if (Array.isArray(result?.rejected)) {
|
|
16878
|
+
rejected.push(...result.rejected);
|
|
16879
|
+
}
|
|
16880
|
+
}
|
|
15718
16881
|
if (acceptedCount === 0 && rejected.length > 0) {
|
|
15719
16882
|
const detail = rejected.map(
|
|
15720
16883
|
(entry) => `${entry.name || "unknown"}: ${entry.reason || "rejected"}`
|
|
@@ -16940,6 +18103,7 @@ var HARNESS_V3_FRONTEND_ACTIONS_MODULE = "@granular/actions/frontend";
|
|
|
16940
18103
|
var HARNESS_V3_CSV_MODULE = "@granular/utils/csv";
|
|
16941
18104
|
var HARNESS_V3_XLSX_MODULE = "@granular/utils/xlsx";
|
|
16942
18105
|
var LEGACY_SANDBOX_TOOLS_MODULE_PATTERN = "\\.\\/sandbox-tools(?:\\.js)?";
|
|
18106
|
+
var HARNESS_V3_RUNTIME_MODULE_PATTERN = "@granular/(?:agent|session|domain(?:/[A-Za-z_$][\\w$]*)?|actions/(?:backend|frontend)|utils/(?:csv|xlsx))";
|
|
16943
18107
|
function hasNamedModuleImport(source, moduleName, name) {
|
|
16944
18108
|
const escapedModule = moduleName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
16945
18109
|
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -16998,6 +18162,15 @@ function reviewGeneratedJobCode(code, _options = {}) {
|
|
|
16998
18162
|
message: "Generated code must use static top-level ESM imports from the Harness v3 runtime modules. Do not use dynamic import(...)."
|
|
16999
18163
|
});
|
|
17000
18164
|
}
|
|
18165
|
+
if (new RegExp(
|
|
18166
|
+
`import\\s+\\*\\s+as\\s+[A-Za-z_$][\\w$]*\\s+from\\s*['"]${HARNESS_V3_RUNTIME_MODULE_PATTERN}['"]`
|
|
18167
|
+
).test(normalized)) {
|
|
18168
|
+
issues.push({
|
|
18169
|
+
code: "runtime_namespace_import",
|
|
18170
|
+
severity: "error",
|
|
18171
|
+
message: 'Generated code must use named imports from Harness runtime modules. Do not use namespace imports such as `import * as agent from "@granular/agent"`; use `import { replyToUser, artifacts } from "@granular/agent"`.'
|
|
18172
|
+
});
|
|
18173
|
+
}
|
|
17001
18174
|
if (/\bprocess\.exit\s*\(/.test(normalized)) {
|
|
17002
18175
|
issues.push({
|
|
17003
18176
|
code: "process_exit",
|
|
@@ -18017,6 +19190,31 @@ function buildGranularAgentHeapBlock(heapSummary) {
|
|
|
18017
19190
|
entries: {}
|
|
18018
19191
|
});
|
|
18019
19192
|
}
|
|
19193
|
+
function buildGranularAgentManualActionMemorySummary(input) {
|
|
19194
|
+
const maxItems = Math.max(1, Math.min(12, input.maxItems ?? 8));
|
|
19195
|
+
const suggestions = (input.suggestions || []).filter((suggestion) => suggestion?.actionKey).slice(0, maxItems).map((suggestion) => ({
|
|
19196
|
+
actionKey: suggestion.actionKey,
|
|
19197
|
+
label: suggestion.label || null,
|
|
19198
|
+
targetClassName: suggestion.targetClassName || null,
|
|
19199
|
+
count: typeof suggestion.count === "number" && Number.isFinite(suggestion.count) ? suggestion.count : null,
|
|
19200
|
+
subjectCount: typeof suggestion.subjectCount === "number" && Number.isFinite(suggestion.subjectCount) ? suggestion.subjectCount : null,
|
|
19201
|
+
successCount: typeof suggestion.successCount === "number" && Number.isFinite(suggestion.successCount) ? suggestion.successCount : null,
|
|
19202
|
+
failureCount: typeof suggestion.failureCount === "number" && Number.isFinite(suggestion.failureCount) ? suggestion.failureCount : null,
|
|
19203
|
+
lastOccurredAt: typeof suggestion.lastOccurredAt === "number" && Number.isFinite(suggestion.lastOccurredAt) ? suggestion.lastOccurredAt : null,
|
|
19204
|
+
sampleTargetIds: Array.isArray(suggestion.sampleTargetIds) ? suggestion.sampleTargetIds.filter(
|
|
19205
|
+
(id) => typeof id === "string" && id.trim().length > 0
|
|
19206
|
+
).slice(0, 6) : []
|
|
19207
|
+
}));
|
|
19208
|
+
return [
|
|
19209
|
+
renderConstBlock("manualActionMemory", {
|
|
19210
|
+
suggestions
|
|
19211
|
+
}),
|
|
19212
|
+
"Use manualActionMemory only as behavioral context for likely next actions. Ground the current target and validate permissions before creating or running prepared actions."
|
|
19213
|
+
].join("\n");
|
|
19214
|
+
}
|
|
19215
|
+
function buildGranularAgentManualActionBlock(manualActionSummary) {
|
|
19216
|
+
return manualActionSummary?.trim() || buildGranularAgentManualActionMemorySummary({ suggestions: [] });
|
|
19217
|
+
}
|
|
18020
19218
|
function projectSessionFileSummary(liveDoc) {
|
|
18021
19219
|
const files = asRecord4(liveDoc?.files);
|
|
18022
19220
|
const byId = asRecord4(files?.byId) || {};
|
|
@@ -18048,8 +19246,12 @@ function buildGranularAgentFileBlock(fileSummary) {
|
|
|
18048
19246
|
function extractRuntimeContractExports(domainBlock) {
|
|
18049
19247
|
const classes = /* @__PURE__ */ new Set();
|
|
18050
19248
|
const actions = /* @__PURE__ */ new Set();
|
|
18051
|
-
const
|
|
18052
|
-
for (const match of domainBlock.matchAll(
|
|
19249
|
+
const classConstPattern = /export\s+declare\s+const\s+([A-Za-z_$][\w$]*)\s*:\s*EntityClass\b/g;
|
|
19250
|
+
for (const match of domainBlock.matchAll(classConstPattern)) {
|
|
19251
|
+
classes.add(match[1]);
|
|
19252
|
+
}
|
|
19253
|
+
const classDeclPattern = /export\s+declare\s+class\s+([A-Za-z_$][\w$]*)\b/g;
|
|
19254
|
+
for (const match of domainBlock.matchAll(classDeclPattern)) {
|
|
18053
19255
|
classes.add(match[1]);
|
|
18054
19256
|
}
|
|
18055
19257
|
const actionPattern = /export\s+declare\s+function\s+([A-Za-z_$][\w$]*)/g;
|
|
@@ -18554,6 +19756,9 @@ function buildGranularAgentSystemPrompt(input) {
|
|
|
18554
19756
|
});
|
|
18555
19757
|
const referentBlock = buildGranularAgentReferentBlock(input.referentSummary);
|
|
18556
19758
|
const loopBlock = buildGranularAgentLoopBlock(input.loopSummary);
|
|
19759
|
+
const manualActionBlock = buildGranularAgentManualActionBlock(
|
|
19760
|
+
input.manualActionSummary
|
|
19761
|
+
);
|
|
18557
19762
|
const knownFactsBlock = renderConstBlock(
|
|
18558
19763
|
"knownFacts",
|
|
18559
19764
|
buildKnownFactsFromCheckpoint(input.checkpoint)
|
|
@@ -18567,16 +19772,16 @@ function buildGranularAgentSystemPrompt(input) {
|
|
|
18567
19772
|
- \`replyToUser(...)\` displays text directly to the user in the host UI. Treat it as the user-facing progress and reply channel, not as a debug log.
|
|
18568
19773
|
- For long-running or multi-step jobs, send several short \`replyToUser(...)\` updates as useful milestones are reached so the user can see what is happening instead of waiting in silence.
|
|
18569
19774
|
- Write \`replyToUser(...)\` content in a friendly, readable product-assistant style: concrete, concise, and natural. Avoid robotic status dumps, raw implementation names, and unexplained IDs unless the ID helps the user.
|
|
18570
|
-
- When \`replyToUser(...)\` mentions a grounded record that should remain clickable/referable, wrap only the visible record label in a self-closing inline reference tag
|
|
18571
|
-
- Treat \`showObjects(...)\` as the UI display call for user-visible records,
|
|
18572
|
-
-
|
|
18573
|
-
-
|
|
18574
|
-
-
|
|
18575
|
-
-
|
|
19775
|
+
- When \`replyToUser(...)\` mentions a grounded record that should remain clickable/referable, wrap only the visible record label in a self-closing inline reference tag using the actual class name and id/path.
|
|
19776
|
+
- Treat \`showObjects(...)\` as the UI display call for user-visible records. When records should remain reusable for follow-ups, first save the runtime record or ordered record array with \`await groundedObjects.save("stable_selection_name", value)\`, then display it once with \`showObjects({ variableNames: ["stable_selection_name"] })\`.
|
|
19777
|
+
- \`groundedObjects.save(...)\` only accepts scalar values, session files, runtime records/sandbox instances, or arrays of runtime records/sandbox instances from one class. Do not save plain action/effect result objects; fetch affected records first or answer from summaries with \`replyToUser(...)\`.
|
|
19778
|
+
- Do not use \`showObjects({ entries: [...] })\` or \`showObjects({ saveAs, entries })\`. Save ordered pages, queues, search results, or ranked lists with \`groundedObjects.save(...)\`, then call \`showObjects({ variableNames: [...] })\` once.
|
|
19779
|
+
- Use \`showAgentResponse({ reply, show: [record, action] })\` when one assistant message should combine text, grounded records, files, prepared actions, or action suggestions. The \`action\` can be a state handle such as \`record.lifecycle.approved\` or an action handle such as \`record.lifecycle.approved.reach()\`.
|
|
19780
|
+
- Pass grounded records directly in \`show\` when the default record presentation answers the request. When the user asks for particular columns, comparisons, or computed values, import \`table\` (and \`relativeTime\` when useful) from \`@granular/agent\` and call \`showAgentResponse({ reply, show: table(records, [{ label: "Object", value: record => record.label }, { label: "When", value: record => relativeTime(record.timestamp) }]) })\`. Column callbacks must be synchronous and return a scalar, \`Date\`, or \`relativeTime(...)\`; they run inside the job and only resolved cells are persisted.
|
|
19781
|
+
- Do not use deprecated side-channel helpers such as \`agent_text_message(...)\`, \`agent_heap_objects(...)\`, or \`agent_message(...)\` unless the generated types expose no Harness v3 helper alternative.
|
|
18576
19782
|
- When the user asks to show, list, display, open, or "show them" for records you found, call \`showObjects(...)\`; do not answer only with a count or text summary.
|
|
18577
19783
|
- For count-only questions such as "how many", "how many X do I have", or "what is the total number of X", call the entity \`.count(...)\` or use page \`totalCount\` only when a page is already needed for other reasons. Answer with \`replyToUser(...)\` only. Do not call \`showObjects(...)\`, \`saveAs\`, or \`groundedObjects.save(...)\` unless the user also asked to see records or a later requested action needs a reusable record selection.
|
|
18578
|
-
-
|
|
18579
|
-
- For ordered record slices, pages, queues, search results, or ranked lists, save the slice with \`groundedObjects.save(...)\` and then call \`showObjects({ variableNames: [...] })\` once. Use a stable name that preserves the slice identity and ordering so later references such as "the second item" or "back on the first slice" resolve to the correct earlier slice, not merely the most recent record.
|
|
19784
|
+
- Use stable saved list names that preserve identity and ordering so later references such as "the second item" or "back on the first slice" resolve to the correct earlier slice, not merely the most recent record.
|
|
18580
19785
|
- Do not rely on the final return value for UI output. Do not return ad-hoc \`reply\` / \`show\` payloads instead of explicit agent message calls.` : `- Every job that answers the user must emit \`replyToUser(...)\` from \`@granular/agent\`.
|
|
18581
19786
|
- \`replyToUser(...)\` displays text directly to the user in the host UI. Treat it as the user-facing progress and reply channel, not as a debug log.
|
|
18582
19787
|
- For long-running or multi-step jobs, send several short \`replyToUser(...)\` updates as useful milestones are reached so the user can see what is happening instead of waiting in silence.
|
|
@@ -18587,7 +19792,7 @@ function buildGranularAgentSystemPrompt(input) {
|
|
|
18587
19792
|
- When using code, assistant text must be empty or one brief summary.
|
|
18588
19793
|
- Code must be plain runnable JavaScript with top-level await.
|
|
18589
19794
|
- Use [Runtime Imports] as the authoritative module map. Import only listed module exports.
|
|
18590
|
-
- Use static top-level imports such as \`import { Foo } from "@granular/domain/Foo"; import { replyToUser } from "@granular/agent";\`. Do not use dynamic imports for runtime modules.
|
|
19795
|
+
- Use static top-level named imports such as \`import { Foo } from "@granular/domain/Foo"; import { replyToUser, artifacts } from "@granular/agent";\`. Do not use dynamic imports or namespace imports like \`import * as agent from "@granular/agent"\` for runtime modules.
|
|
18591
19796
|
- Read and write session files through the virtual filesystem modules listed in [Runtime Imports]. Input files are mounted under \`/session/input\`; files written under \`/session/output\` are persisted as agent-created session files.
|
|
18592
19797
|
- Do not ask the user to provide virtual filesystem paths. Users attach or mention files by name in the UI; resolve the right file from \`sessionFileManifest.files\` or the current attachment context, then use its provided path internally.
|
|
18593
19798
|
- The \`sessionFileManifest\` block is prompt context, not an imported module or runtime variable. For dynamic file lookup, import \`files\` from \`@granular/session\` and match \`filename\` to a returned file's \`path\`.
|
|
@@ -18650,6 +19855,7 @@ ${workflowRules}
|
|
|
18650
19855
|
High-priority execution rules:
|
|
18651
19856
|
- Treat a human reference as something to ground, not as missing data. When the user names or describes a record, group, queue, parent, relationship, or prior result and asks to inspect, decide, update, schedule, approve, send, or otherwise act on session data, run a code job to ground it before asking the user for more details.
|
|
18652
19857
|
- For a human-described primary anchor, a no-match answer is only justified after more than one distinct grounding attempt, such as owner/container grounding, relationship traversal, exact id/path lookup, or shorter target-local search. Before the primary no-match return, retry that same anchor with fewer text constraints or a distinct grounding strategy; do not stop after one zero-result list/find/page call.
|
|
19858
|
+
- If the latest user wording names an entity type that has an importable class, ground that class first. A previously installed parent class or related action method is not a substitute; after zero results in one class, pivot to the latest named importable class before reporting no match.
|
|
18653
19859
|
- A confirmation requirement is not a reason to stay text-only. Do all safe read-only grounding and availability/status checks first, then call \`userInteraction.askConfirmation(...)\` or \`userInteraction.askChoice(...)\` before the mutation.
|
|
18654
19860
|
- In any code branch where a requested action or mutation has multiple possible targets, import \`userInteraction\` from \`@granular/session\` and use \`await userInteraction.askChoice(...)\` in that branch. This includes ambiguity discovered after a query returns several records. A branch that only shows candidates, asks in text, and returns leaves the requested action unfinished.
|
|
18655
19861
|
- Before any mutation, know whether the target is one record or several. A singular phrase like "the item" is not proof of uniqueness after a query finds multiple matching records. If the user did not give an exact identifier or explicit selection criterion, call \`userInteraction.askChoice({ options, ... })\` with grounded choices; do not choose by age, amount, priority, order, or convenience on your own. Resolve the target before any yes/no confirmation.
|
|
@@ -18693,7 +19899,7 @@ Intent resolution:
|
|
|
18693
19899
|
- For follow-up words like "other", "another", or "remaining" after the user selected one candidate from a previous choice, resolve within the active contrast from that choice and the user's answer. Exclude the selected item, preserve descriptors such as larger, smaller, next, older, different, or same status, and do not take the first leftover from a wider saved list when the contrast narrows the intended set.
|
|
18694
19900
|
- Before any mutation, prove the target resolves to exactly one grounded record. If the request describes a set, category, relationship, prior result group, or other non-unique scope, gather the candidate records first; when more than one candidate remains, ask the user to choose before calling the action.
|
|
18695
19901
|
- For ambiguous choice prompts before a mutation, every option that describes a different candidate must carry a distinct grounded record value/path. After the answer, do not fall back to the first candidate if matching fails; ask again or stop without mutating.
|
|
18696
|
-
- The [State] constants and [Runtime Imports] map are prompt context, not runtime variables. Never reference \`runtimeImports\`, \`savedData\`, \`sessionFileManifest\`, \`recentReferences\`, \`workflowContext\`, \`workflowState\`, or \`capabilities\` as variables in generated code. When using a recent reference or file path, copy
|
|
19902
|
+
- The [State] constants and [Runtime Imports] map are prompt context, not runtime variables. Never reference \`runtimeImports\`, \`savedData\`, \`manualActionMemory\`, \`sessionFileManifest\`, \`recentReferences\`, \`workflowContext\`, \`workflowState\`, or \`capabilities\` as variables in generated code. When using a recent reference, manual-action pattern, or file path, copy concrete ids/values into code and fetch/read it with the relevant runtime API.
|
|
18697
19903
|
- Never write placeholder grounding code such as \`const path = null\`, \`const groundedPath = ""\`, or \`const recordPath = ""\`. If no saved reference is available, delete that branch entirely and execute the fallback lookup directly.
|
|
18698
19904
|
- Never call \`.get({ path: "" })\`; an empty path is not a saved reference.
|
|
18699
19905
|
- For ordinal references to earlier pages, slices, lists, or ranked results, use the saved list/recent references first. If no saved list is available, rerun the exact same ordered query and select the ordinal index from its returned \`items\`; never invent a record path from a label or ordinal.
|
|
@@ -18820,20 +20026,10 @@ Ask the user when:
|
|
|
18820
20026
|
- the target is unique but the requested action is unclear
|
|
18821
20027
|
|
|
18822
20028
|
Relationship filters:
|
|
18823
|
-
-
|
|
18824
|
-
-
|
|
18825
|
-
-
|
|
18826
|
-
-
|
|
18827
|
-
- Do not invent nested operators under relationship fields. A one-record relationship filter accepts only its documented operators such as \`id\`, \`path\`, \`is\`, \`null\`, and \`not_null\`; deeper conditions must go under \`is\` or be handled by fetching records and following getters.
|
|
18828
|
-
- Never use \`some\` on one-record fields. If the generated TypeScript type says \`OneRelationFilter\`, valid operators are \`id\`, \`path\`, \`is\`, \`null\`, and \`not_null\`; \`some\` is invalid.
|
|
18829
|
-
- Use \`some\` only when the generated TypeScript type says \`ManyRelationFilter\`.
|
|
18830
|
-
- For a singular relationship that points to an intermediate record, nested filters still use \`is\` at the singular hop. Do not use \`some\` because the nested condition names another related record.
|
|
18831
|
-
- Use \`{ relationship: { id: "record_id" } }\` or \`{ relationship: { path: "class_record_id" } }\` when matching a known related record.
|
|
18832
|
-
- When you already fetched the related record, use \`{ relationship: { path: record._graphPath } }\` or \`{ relationship: { id: record.id } }\`; do not wrap a known id/path under \`is\`.
|
|
18833
|
-
- The path used in a relationship filter must be the path of the relationship target. For same-queue follow-ups from an item/batch/ticket, fetch that item's related unit/site/depot first and use the related unit/site/depot path; do not use the item path as a unit/site/depot path.
|
|
18834
|
-
- Use \`{ relationship: { is: { field: { equal_to: value } } } }\` only for nested field filters. Never put \`id\` or \`path\` inside \`is\`.
|
|
18835
|
-
- Do not write \`{ relationship: { some: ... } }\` unless the generated filter type for that exact relationship says it is a many/collection relationship. For one-record, parent, owner, or many-to-one relationships, use \`path\`, \`id\`, \`is\`, or getter traversal.
|
|
18836
|
-
- Do not pass a full record instance into a filter; if you already fetched a record, filter by its id or path instead.
|
|
20029
|
+
- Use the generated filter type as the authority: \`OneRelationFilter\` supports \`id\`, \`path\`, \`is\`, \`null\`, \`not_null\`; \`ManyRelationFilter\` supports those plus \`some\`.
|
|
20030
|
+
- Use \`id\` or \`path\` for a known related record; use \`is\` or \`some\` only for nested target-field filters.
|
|
20031
|
+
- Relationship paths must belong to the relationship target type. If you have a parent/container/item of another type, fetch the declared related record first and filter with that related record's id/path.
|
|
20032
|
+
- Never pass a full record instance into a filter. Use its id/path or a declared relationship getter.
|
|
18837
20033
|
${domainSections.docs ? `
|
|
18838
20034
|
Domain notes:
|
|
18839
20035
|
${domainSections.docs}
|
|
@@ -18844,6 +20040,24 @@ ${actionIndex}
|
|
|
18844
20040
|
- Global backend actions are executable functions exported by \`@granular/actions/backend\`; import each backend action you call, e.g. \`import { some_action } from "@granular/actions/backend"; await some_action(...)\`. Frontend actions are exported by \`@granular/actions/frontend\` when the action index marks them as frontend actions.
|
|
18845
20041
|
- Actions listed under "Record-level" are instance methods. First fetch or find the specific record, then call the action on that instance, e.g. \`const item = await Item.get({ path }); await item.action_name(...)\`.
|
|
18846
20042
|
- Actions listed under "Class-level" are class/static methods. Call them on the imported class, e.g. \`await Item.action_name(...)\`.
|
|
20043
|
+
- State-machine handles are the preferred workflow surface. For an existing record, use \`record.lifecycle.targetState\`; for a not-yet-created record, use class-level handles such as \`SpendRequest.lifecycle.policy_review\` only to prepare a provisional new-record action flow.
|
|
20044
|
+
- For pure field-collection requests, target the class-level entry state handle; for submit/review requests, target the nearest requested later state.
|
|
20045
|
+
- Choose the nearest target state that matches the user's words. Do not aim at a later state just because it is reachable.
|
|
20046
|
+
- For grounded stateful records, choose the target state handle from the user's requested outcome and handle docs. Use \`plan()\`, \`blockers()\`, or \`permissions()\` to explain reachability, missing requirements, stale state, and who can run the action; do not derive workflow meaning or adjacent actions from raw \`record.status\` or \`current().state\`.
|
|
20047
|
+
- Before opening a stateful action, call \`const plan = await handle.plan()\`. If the plan or opened artifact shows blockers, stale state, missing relationships, permissions, or no reachable path, explain those structured results and display the record/artifact. Do not skip to a later state or build your own status ladder.
|
|
20048
|
+
- Use typed plan fields and helpers directly. For visible blocker text, use the declared blocker-formatting helper when available instead of hand-written object casts.
|
|
20049
|
+
- A suggestion is a recommendation button: \`await stateHandle.suggest(message)\`. A prepared action is the editable form the user can review/run: \`const action = stateHandle.reach(); const prepared = await action.open()\`.
|
|
20050
|
+
- Use suggestions when the user asks "what can I do next?", asks for options, or gives an unclear intent. Prefer 2 to 5 concrete suggestions and keep text short.
|
|
20051
|
+
- Opening a prepared action does not execute the underlying mutation. Use \`.open()\` only when the user asks to prepare, review, show, or edit an action before running it, when a new record must be prepared, or when a reviewable form must collect missing editable inputs. Prefill only grounded values and leave unknown fields empty.
|
|
20052
|
+
- When the user explicitly commands an existing-record mutation such as approve, reject, block, route, send, or update, and one visible domain action uniquely matches, call that domain action directly. Do not substitute a state-handle \`.open()\` artifact for execution. If runtime policy requires confirmation, invoke the action once and let the runtime pause and resume that same invocation.
|
|
20053
|
+
- When a grounded related/context record can satisfy the prepared action through declared relationships, use those relationships to fill required relationship inputs before opening or updating the action. If a required relationship remains empty, continue through declared relationship chains from the grounded object when the next hop can fill that slot. Do not only save the context record in memory while leaving derivable relationship slots blank.
|
|
20054
|
+
- A derived intermediate relationship is not enough when another required relationship is still reachable from it. For example, if a team gives a cost center and the action also requires a budget, traverse the cost center's declared budget relationship before opening the action.
|
|
20055
|
+
- If the user says they have a document/work item/event but no matching record is found, check for a declared class-level new-record state/action handle or importable backend create/preparation action for that named class before giving up. Use grounded required fields to open the prepared action or call the create/preparation action; if required values are still missing and no prepared action can collect them, ask only for those values. Do not claim the record already exists.
|
|
20056
|
+
- Do not ask for confirmation before opening a prepared action requested for review; the artifact is itself reviewable. For a direct mutation command, do not open an artifact merely to obtain confirmation. Use \`userInteraction.askConfirmation\` only when the user explicitly asks for a separate yes/no step, material ambiguity remains after grounding, or policy requires confirmation outside the invoked action runtime.
|
|
20057
|
+
- If the action cannot continue because of missing input, stale state, missing relationships, related-state requirements, or permissions, keep/show the prepared action at that blocker and explain the next needed person, record, or value. Do not skip workflow steps or target a later state.
|
|
20058
|
+
- Reuse an already-open prepared action for the same target/action when available: update it, show it again, or explain what is still needed instead of creating a duplicate.
|
|
20059
|
+
- If an open prepared action needs edits, prefer the returned record helper: \`const prepared = await action.open(); await prepared.updateInputs({ inputValues, relationships });\`. Use \`artifacts.updateInputs(id, patch)\` only when you only have an id.
|
|
20060
|
+
- Use \`await prepared.show()\` or \`await actions.show(prepared)\` only to display an already-created prepared action again.
|
|
18847
20061
|
- The action index is the visibility contract. If an action is listed for a class, call it directly on fetched/listed instances of that class; do not use \`typeof record.action_name === "function"\` as a discovery gate. If an action is not listed, do not call it.
|
|
18848
20062
|
- Never call a record-level action as \`Class.action_name(...)\`; that method will not exist.
|
|
18849
20063
|
- For action inputs, use the exact property names from the generated TypeScript method signature or the input schema shown in the action list. Do not invent synonym fields for required inputs.
|
|
@@ -18874,6 +20088,8 @@ ${loopBlock}
|
|
|
18874
20088
|
|
|
18875
20089
|
${knownFactsBlock}
|
|
18876
20090
|
|
|
20091
|
+
${manualActionBlock}
|
|
20092
|
+
|
|
18877
20093
|
[Request]
|
|
18878
20094
|
${input.request?.trim() || "Use the latest user message in the conversation."}`;
|
|
18879
20095
|
}
|
|
@@ -19029,8 +20245,9 @@ function resolveHarnessTemplate(templateId = "stable", options) {
|
|
|
19029
20245
|
}
|
|
19030
20246
|
|
|
19031
20247
|
// src/openai-usage.ts
|
|
19032
|
-
var
|
|
19033
|
-
var
|
|
20248
|
+
var OPENAI_GPT_5_4_PRICING_SOURCE_URL = "https://developers.openai.com/api/docs/models/gpt-5.4/";
|
|
20249
|
+
var OPENAI_GPT_5_6_PRICING_SOURCE_URL = "https://developers.openai.com/api/docs/pricing";
|
|
20250
|
+
var OPENAI_LONG_CONTEXT_THRESHOLD_TOKENS = 272e3;
|
|
19034
20251
|
var OPENAI_MODEL_PRICING_USD_PER_MILLION = {
|
|
19035
20252
|
"gpt-5.4": {
|
|
19036
20253
|
provider: "openai",
|
|
@@ -19039,8 +20256,26 @@ var OPENAI_MODEL_PRICING_USD_PER_MILLION = {
|
|
|
19039
20256
|
inputUsdPerMillion: 2.5,
|
|
19040
20257
|
cachedInputUsdPerMillion: 0.25,
|
|
19041
20258
|
outputUsdPerMillion: 15,
|
|
19042
|
-
sourceUrl:
|
|
19043
|
-
effectiveDate:
|
|
20259
|
+
sourceUrl: OPENAI_GPT_5_4_PRICING_SOURCE_URL,
|
|
20260
|
+
effectiveDate: "2026-05-19"
|
|
20261
|
+
},
|
|
20262
|
+
"gpt-5.6-luna": {
|
|
20263
|
+
provider: "openai",
|
|
20264
|
+
model: "gpt-5.6-luna",
|
|
20265
|
+
currency: "USD",
|
|
20266
|
+
inputUsdPerMillion: 1,
|
|
20267
|
+
cachedInputUsdPerMillion: 0.1,
|
|
20268
|
+
cacheWriteUsdPerMillion: 1.25,
|
|
20269
|
+
outputUsdPerMillion: 6,
|
|
20270
|
+
sourceUrl: OPENAI_GPT_5_6_PRICING_SOURCE_URL,
|
|
20271
|
+
effectiveDate: "2026-07-11",
|
|
20272
|
+
longContextThresholdTokens: OPENAI_LONG_CONTEXT_THRESHOLD_TOKENS,
|
|
20273
|
+
longContextPricing: {
|
|
20274
|
+
inputUsdPerMillion: 2,
|
|
20275
|
+
cachedInputUsdPerMillion: 0.2,
|
|
20276
|
+
cacheWriteUsdPerMillion: 2.5,
|
|
20277
|
+
outputUsdPerMillion: 9
|
|
20278
|
+
}
|
|
19044
20279
|
}
|
|
19045
20280
|
};
|
|
19046
20281
|
function asRecord5(value) {
|
|
@@ -19053,8 +20288,18 @@ function numberField(record, key) {
|
|
|
19053
20288
|
function microsPerMillion(usdPerMillion) {
|
|
19054
20289
|
return Math.round(usdPerMillion * 1e6);
|
|
19055
20290
|
}
|
|
19056
|
-
function getOpenAIModelPricing(model) {
|
|
19057
|
-
|
|
20291
|
+
function getOpenAIModelPricing(model, inputTokens = 0) {
|
|
20292
|
+
const pricing = OPENAI_MODEL_PRICING_USD_PER_MILLION[model];
|
|
20293
|
+
if (!pricing) return null;
|
|
20294
|
+
const threshold = pricing.longContextThresholdTokens ?? null;
|
|
20295
|
+
if (pricing.longContextPricing && typeof threshold === "number" && inputTokens > threshold) {
|
|
20296
|
+
return {
|
|
20297
|
+
...pricing,
|
|
20298
|
+
...pricing.longContextPricing,
|
|
20299
|
+
contextTier: "long"
|
|
20300
|
+
};
|
|
20301
|
+
}
|
|
20302
|
+
return { ...pricing, contextTier: "short" };
|
|
19058
20303
|
}
|
|
19059
20304
|
function normalizeOpenAIUsage(rawUsage) {
|
|
19060
20305
|
const usage = asRecord5(rawUsage);
|
|
@@ -19062,6 +20307,7 @@ function normalizeOpenAIUsage(rawUsage) {
|
|
|
19062
20307
|
return {
|
|
19063
20308
|
inputTokens: 0,
|
|
19064
20309
|
cachedInputTokens: 0,
|
|
20310
|
+
cacheWriteTokens: 0,
|
|
19065
20311
|
uncachedInputTokens: 0,
|
|
19066
20312
|
outputTokens: 0,
|
|
19067
20313
|
reasoningTokens: 0,
|
|
@@ -19077,20 +20323,28 @@ function normalizeOpenAIUsage(rawUsage) {
|
|
|
19077
20323
|
inputTokens,
|
|
19078
20324
|
numberField(inputDetails, "cached_tokens") || numberField(inputDetails, "cached_input_tokens")
|
|
19079
20325
|
);
|
|
20326
|
+
const cacheWriteTokens = Math.min(
|
|
20327
|
+
Math.max(inputTokens - cachedInputTokens, 0),
|
|
20328
|
+
numberField(inputDetails, "cache_write_tokens")
|
|
20329
|
+
);
|
|
19080
20330
|
const reasoningTokens = numberField(outputDetails, "reasoning_tokens") || numberField(outputDetails, "reasoning_output_tokens");
|
|
19081
20331
|
return {
|
|
19082
20332
|
inputTokens,
|
|
19083
20333
|
cachedInputTokens,
|
|
19084
|
-
|
|
20334
|
+
cacheWriteTokens,
|
|
20335
|
+
uncachedInputTokens: Math.max(
|
|
20336
|
+
inputTokens - cachedInputTokens - cacheWriteTokens,
|
|
20337
|
+
0
|
|
20338
|
+
),
|
|
19085
20339
|
outputTokens,
|
|
19086
20340
|
reasoningTokens,
|
|
19087
20341
|
totalTokens
|
|
19088
20342
|
};
|
|
19089
20343
|
}
|
|
19090
20344
|
function calculateOpenAITokenSpend(model, rawUsage) {
|
|
19091
|
-
const pricing = getOpenAIModelPricing(model);
|
|
19092
|
-
if (!pricing) return null;
|
|
19093
20345
|
const usage = normalizeOpenAIUsage(rawUsage);
|
|
20346
|
+
const pricing = getOpenAIModelPricing(model, usage.inputTokens);
|
|
20347
|
+
if (!pricing) return null;
|
|
19094
20348
|
const inputPricePerMillionMicros = microsPerMillion(
|
|
19095
20349
|
pricing.inputUsdPerMillion
|
|
19096
20350
|
);
|
|
@@ -19100,14 +20354,19 @@ function calculateOpenAITokenSpend(model, rawUsage) {
|
|
|
19100
20354
|
const outputPricePerMillionMicros = microsPerMillion(
|
|
19101
20355
|
pricing.outputUsdPerMillion
|
|
19102
20356
|
);
|
|
20357
|
+
const cacheWritePricePerMillionMicros = typeof pricing.cacheWriteUsdPerMillion === "number" ? microsPerMillion(pricing.cacheWriteUsdPerMillion) : null;
|
|
20358
|
+
const cacheWriteCostMicros = Math.round(
|
|
20359
|
+
usage.cacheWriteTokens * (cacheWritePricePerMillionMicros ?? inputPricePerMillionMicros) / 1e6
|
|
20360
|
+
);
|
|
19103
20361
|
const amountMicros = Math.round(
|
|
19104
|
-
(usage.uncachedInputTokens * inputPricePerMillionMicros + usage.cachedInputTokens * cachedInputPricePerMillionMicros + usage.outputTokens * outputPricePerMillionMicros) / 1e6
|
|
20362
|
+
(usage.uncachedInputTokens * inputPricePerMillionMicros + usage.cachedInputTokens * cachedInputPricePerMillionMicros + usage.cacheWriteTokens * (cacheWritePricePerMillionMicros ?? inputPricePerMillionMicros) + usage.outputTokens * outputPricePerMillionMicros) / 1e6
|
|
19105
20363
|
);
|
|
19106
20364
|
return {
|
|
19107
20365
|
provider: "openai",
|
|
19108
20366
|
model,
|
|
19109
20367
|
inputTokens: usage.inputTokens,
|
|
19110
20368
|
cachedInputTokens: usage.cachedInputTokens,
|
|
20369
|
+
cacheWriteTokens: usage.cacheWriteTokens,
|
|
19111
20370
|
uncachedInputTokens: usage.uncachedInputTokens,
|
|
19112
20371
|
outputTokens: usage.outputTokens,
|
|
19113
20372
|
reasoningTokens: usage.reasoningTokens,
|
|
@@ -19116,7 +20375,11 @@ function calculateOpenAITokenSpend(model, rawUsage) {
|
|
|
19116
20375
|
currency: "USD",
|
|
19117
20376
|
inputPricePerMillionMicros,
|
|
19118
20377
|
cachedInputPricePerMillionMicros,
|
|
20378
|
+
cacheWritePricePerMillionMicros,
|
|
20379
|
+
cacheWriteCostMicros,
|
|
19119
20380
|
outputPricePerMillionMicros,
|
|
20381
|
+
pricingContextTier: pricing.contextTier || "short",
|
|
20382
|
+
longContextThresholdTokens: pricing.longContextThresholdTokens ?? null,
|
|
19120
20383
|
pricingSource: pricing.sourceUrl,
|
|
19121
20384
|
pricingEffectiveAt: pricing.effectiveDate,
|
|
19122
20385
|
usage
|
|
@@ -19624,6 +20887,8 @@ function modelOutputInstruction() {
|
|
|
19624
20887
|
'Do not use "action":"reply" to say a record is not grounded yet; if the request names or describes a domain record, use "action":"job" and ground it from session state, relationships, searches, or visible read-only actions first.',
|
|
19625
20888
|
'Before claiming you lack access, inspect the visible action list. If a visible read-only search, lookup, list, guidance, note, policy, or knowledge action can satisfy a "check", "find", "look up", or "whether we have guidance" request, choose "action":"job" and call it.',
|
|
19626
20889
|
"Generated code must not report no matches for the primary human-described anchor after a single zero-result list/find/page call. Before that primary no-match return, retry the primary anchor with fewer text constraints or a distinct fallback such as owner/container grounding, relationship traversal, exact-id/path lookup, or shorter target-local search.",
|
|
20890
|
+
"If the latest request names an importable entity type, generated code must ground that class first; a previously installed parent class or related action method is not a substitute. After zero results in one class, pivot to the latest named importable class before reporting no match.",
|
|
20891
|
+
"When the user says they have a document/work item/event but no matching record exists yet, generated code must check the visible class-level new-record/create/preparation surface before returning no-match; if required create inputs are still missing and no prepared action can collect them, ask only for those inputs.",
|
|
19627
20892
|
'Use "action":"job" when the next step should run code or mutate workflow state.',
|
|
19628
20893
|
'When action is "job", include runnable code in "code" and emit the "code" field before any non-empty "reply" field so generated code comments can stream as progress.',
|
|
19629
20894
|
"Generated job code must use plain ASCII punctuation in string literals and comments. Do not use curly quotes, smart apostrophes, en dashes, em dashes, or other typographic punctuation in code.",
|
|
@@ -19693,7 +20958,12 @@ function createOpenAIChatTurnGenerator(options) {
|
|
|
19693
20958
|
|
|
19694
20959
|
${modelOutputInstruction()}`
|
|
19695
20960
|
},
|
|
19696
|
-
...input.history
|
|
20961
|
+
...input.history.map(
|
|
20962
|
+
(message) => ({
|
|
20963
|
+
role: message.role,
|
|
20964
|
+
content: message.content
|
|
20965
|
+
})
|
|
20966
|
+
),
|
|
19697
20967
|
{ role: "user", content: input.request }
|
|
19698
20968
|
];
|
|
19699
20969
|
const payload = {
|
|
@@ -19712,11 +20982,12 @@ ${modelOutputInstruction()}`
|
|
|
19712
20982
|
let usage = null;
|
|
19713
20983
|
let requestId = null;
|
|
19714
20984
|
if (input.onTextDelta || input.onReplyDelta || input.onCodeDelta) {
|
|
19715
|
-
const
|
|
20985
|
+
const streamPayload = {
|
|
19716
20986
|
...payload,
|
|
19717
20987
|
stream: true,
|
|
19718
20988
|
stream_options: { include_usage: true }
|
|
19719
|
-
}
|
|
20989
|
+
};
|
|
20990
|
+
const stream = await client.chat.completions.create(streamPayload);
|
|
19720
20991
|
let streamedReply = "";
|
|
19721
20992
|
let streamedCode = "";
|
|
19722
20993
|
const emitReplyDelta = async () => {
|
|
@@ -19742,7 +21013,8 @@ ${modelOutputInstruction()}`
|
|
|
19742
21013
|
await input.onCodeDelta(delta);
|
|
19743
21014
|
};
|
|
19744
21015
|
for await (const event of stream) {
|
|
19745
|
-
|
|
21016
|
+
const eventRecord = event;
|
|
21017
|
+
requestId = requestId || event.id || (typeof eventRecord._request_id === "string" ? eventRecord._request_id : null);
|
|
19746
21018
|
usage = event.usage || usage;
|
|
19747
21019
|
const delta = event.choices?.[0]?.delta?.content;
|
|
19748
21020
|
const deltaText = typeof delta === "string" ? delta : Array.isArray(delta) ? delta.map((part) => asRecord6(part)?.text || "").join("") : "";
|
|
@@ -19756,14 +21028,13 @@ ${modelOutputInstruction()}`
|
|
|
19756
21028
|
await emitCodeDelta();
|
|
19757
21029
|
raw = { streamed: true, model, usage, request_id: requestId };
|
|
19758
21030
|
} else {
|
|
19759
|
-
const completion = await client.chat.completions.create(
|
|
19760
|
-
|
|
19761
|
-
);
|
|
21031
|
+
const completion = await client.chat.completions.create(payload);
|
|
21032
|
+
const completionRecord = completion;
|
|
19762
21033
|
raw = completion;
|
|
19763
21034
|
usage = completion.usage;
|
|
19764
|
-
requestId = (typeof completion.id === "string" ? completion.id : null) || (typeof
|
|
21035
|
+
requestId = (typeof completion.id === "string" ? completion.id : null) || (typeof completionRecord._request_id === "string" ? completionRecord._request_id : null);
|
|
19765
21036
|
const content = asRecord6(
|
|
19766
|
-
asRecord6(completion.choices
|
|
21037
|
+
asRecord6(completion.choices[0])?.message
|
|
19767
21038
|
)?.content;
|
|
19768
21039
|
text = typeof content === "string" ? content : Array.isArray(content) ? content.map((part) => asRecord6(part)?.text || "").join("") : "";
|
|
19769
21040
|
}
|
|
@@ -20100,6 +21371,9 @@ async function generateTurnWithRepair(generator, input) {
|
|
|
20100
21371
|
"",
|
|
20101
21372
|
"The previous generated job code failed preflight review against [Runtime Imports] and the runtime contract.",
|
|
20102
21373
|
"Return a corrected JSON object. Keep the user's requested behavior, but fix every issue below before execution.",
|
|
21374
|
+
"If an issue says workflow path, blockers, permissions, or readiness were derived from raw status fields, remove the status/current-state ladder. Pick the declared state handle that matches the user's requested outcome, call plan()/blockers()/permissions() first, then open/show that handle or explain its structured blockers.",
|
|
21375
|
+
"If an issue says an unsafe type assertion was used, remove the cast and use declared fields or typed helpers directly. For state plans, use plan.blockers, plan.permissions, plan.steps, plan.summary, or blocker-formatting helpers without hand-written object casts.",
|
|
21376
|
+
"If the previous code stopped after no matching record for a user-supplied document/work item, preserve the lookup but check declared new-record/create/preparation surfaces next. Import the backend action module if needed, or ask only for missing required create inputs when no prepared action can collect them.",
|
|
20103
21377
|
"",
|
|
20104
21378
|
"Preflight issues:",
|
|
20105
21379
|
...issues.map((issue) => `- ${issue.code}: ${issue.message}`),
|