@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/index.js
CHANGED
|
@@ -44,11 +44,11 @@ var __export = (target, all) => {
|
|
|
44
44
|
for (var name in all)
|
|
45
45
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
46
46
|
};
|
|
47
|
-
var __copyProps = (to,
|
|
48
|
-
if (
|
|
49
|
-
for (let key of __getOwnPropNames(
|
|
47
|
+
var __copyProps = (to, from2, except, desc) => {
|
|
48
|
+
if (from2 && typeof from2 === "object" || typeof from2 === "function") {
|
|
49
|
+
for (let key of __getOwnPropNames(from2))
|
|
50
50
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
51
|
-
__defProp(to, key, { get: () =>
|
|
51
|
+
__defProp(to, key, { get: () => from2[key], enumerable: !(desc = __getOwnPropDesc(from2, key)) || desc.enumerable });
|
|
52
52
|
}
|
|
53
53
|
return to;
|
|
54
54
|
};
|
|
@@ -4038,6 +4038,9 @@ function rpcTimeoutMsForMethod(method) {
|
|
|
4038
4038
|
return DOMAIN_PACKAGE_RPC_TIMEOUT_MS;
|
|
4039
4039
|
case "client.heartbeat":
|
|
4040
4040
|
case "effects.publishCatalog":
|
|
4041
|
+
case "effects.resetCatalog":
|
|
4042
|
+
case "effects.addCatalog":
|
|
4043
|
+
case "effects.removeCatalog":
|
|
4041
4044
|
case "effects.refresh":
|
|
4042
4045
|
return EFFECT_CONTROL_RPC_TIMEOUT_MS;
|
|
4043
4046
|
case "harness.run":
|
|
@@ -4062,16 +4065,37 @@ var WSClient = class {
|
|
|
4062
4065
|
tokenRefreshTimer = null;
|
|
4063
4066
|
isExplicitlyDisconnected = false;
|
|
4064
4067
|
reconnectAttempts = 0;
|
|
4068
|
+
connectPromise = null;
|
|
4069
|
+
connectionEpoch = 0;
|
|
4070
|
+
cancelConnectAttempt = null;
|
|
4065
4071
|
options;
|
|
4066
4072
|
constructor(options) {
|
|
4067
4073
|
this.options = options;
|
|
4068
4074
|
this.url = options.url;
|
|
4069
4075
|
this.sessionId = options.sessionId;
|
|
4070
4076
|
this.token = options.token;
|
|
4077
|
+
if (options.initialDocumentSnapshot) {
|
|
4078
|
+
this.seedDocumentSnapshot(options.initialDocumentSnapshot);
|
|
4079
|
+
}
|
|
4071
4080
|
}
|
|
4072
4081
|
get currentSessionId() {
|
|
4073
4082
|
return this.sessionId;
|
|
4074
4083
|
}
|
|
4084
|
+
seedDocumentSnapshot(document) {
|
|
4085
|
+
if (!document || typeof document !== "object" || Array.isArray(document)) {
|
|
4086
|
+
return;
|
|
4087
|
+
}
|
|
4088
|
+
try {
|
|
4089
|
+
this.doc = document instanceof Uint8Array ? Automerge__namespace.load(document) : Automerge__namespace.from(document);
|
|
4090
|
+
this.syncState = Automerge__namespace.initSyncState();
|
|
4091
|
+
this.emit("sync", this.doc);
|
|
4092
|
+
} catch (error) {
|
|
4093
|
+
console.warn("[Granular] Failed to seed cached session document", error);
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
saveDocumentSnapshot() {
|
|
4097
|
+
return Automerge__namespace.save(this.doc);
|
|
4098
|
+
}
|
|
4075
4099
|
clearTokenRefreshTimer() {
|
|
4076
4100
|
if (this.tokenRefreshTimer) {
|
|
4077
4101
|
clearTimeout(this.tokenRefreshTimer);
|
|
@@ -4181,8 +4205,23 @@ var WSClient = class {
|
|
|
4181
4205
|
* Connect to the WebSocket server
|
|
4182
4206
|
* @returns {Promise<void>} Resolves when connection is open
|
|
4183
4207
|
*/
|
|
4184
|
-
async connect() {
|
|
4208
|
+
async connect(options = {}) {
|
|
4209
|
+
if (this.ws?.readyState === READY_STATE_OPEN) return;
|
|
4210
|
+
if (this.connectPromise) return this.connectPromise;
|
|
4211
|
+
const connectPromise = this.connectAttempt(options.signal);
|
|
4212
|
+
this.connectPromise = connectPromise;
|
|
4213
|
+
try {
|
|
4214
|
+
await connectPromise;
|
|
4215
|
+
} finally {
|
|
4216
|
+
if (this.connectPromise === connectPromise) {
|
|
4217
|
+
this.connectPromise = null;
|
|
4218
|
+
}
|
|
4219
|
+
}
|
|
4220
|
+
}
|
|
4221
|
+
async connectAttempt(signal) {
|
|
4222
|
+
if (signal?.aborted) throw new Error("WebSocket connect aborted");
|
|
4185
4223
|
const token = await this.resolveTokenForConnect();
|
|
4224
|
+
if (signal?.aborted) throw new Error("WebSocket connect aborted");
|
|
4186
4225
|
this.isExplicitlyDisconnected = false;
|
|
4187
4226
|
this.scheduleTokenRefresh();
|
|
4188
4227
|
if (this.reconnectTimer) {
|
|
@@ -4194,7 +4233,7 @@ var WSClient = class {
|
|
|
4194
4233
|
try {
|
|
4195
4234
|
const wsModule = await Promise.resolve().then(() => (init_wrapper(), wrapper_exports));
|
|
4196
4235
|
WebSocketClass = wsModule.default || wsModule;
|
|
4197
|
-
} catch
|
|
4236
|
+
} catch {
|
|
4198
4237
|
}
|
|
4199
4238
|
}
|
|
4200
4239
|
if (!WebSocketClass) {
|
|
@@ -4202,83 +4241,97 @@ var WSClient = class {
|
|
|
4202
4241
|
'No WebSocket implementation found. If using Node.js, please install "ws" and pass the constructor to the SDK options: { WebSocketCtor: WebSocket }.'
|
|
4203
4242
|
);
|
|
4204
4243
|
}
|
|
4244
|
+
const epoch = ++this.connectionEpoch;
|
|
4245
|
+
const wsUrl = new URL(this.url);
|
|
4246
|
+
wsUrl.searchParams.set("sessionId", this.sessionId);
|
|
4247
|
+
wsUrl.searchParams.set("token", token);
|
|
4248
|
+
const socket = new WebSocketClass(wsUrl.toString());
|
|
4249
|
+
this.ws = socket;
|
|
4205
4250
|
return new Promise((resolve, reject) => {
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
if (
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
this.reconnectTimer = null;
|
|
4218
|
-
}
|
|
4219
|
-
this.reconnectAttempts = 0;
|
|
4220
|
-
this.emit("open", {});
|
|
4221
|
-
resolve();
|
|
4222
|
-
});
|
|
4223
|
-
socket.on("message", (data) => {
|
|
4224
|
-
try {
|
|
4225
|
-
const message = JSON.parse(data.toString());
|
|
4226
|
-
this.handleMessage(message);
|
|
4227
|
-
} catch (error) {
|
|
4228
|
-
console.error("[Granular] Failed to parse message:", error);
|
|
4229
|
-
}
|
|
4230
|
-
});
|
|
4231
|
-
socket.on("error", (error) => {
|
|
4232
|
-
this.emit("error", error);
|
|
4233
|
-
if (socket.readyState !== READY_STATE_OPEN) {
|
|
4234
|
-
reject(error);
|
|
4235
|
-
}
|
|
4236
|
-
});
|
|
4237
|
-
socket.on("close", (code, reason) => {
|
|
4238
|
-
this.handleDisconnect({
|
|
4239
|
-
code,
|
|
4240
|
-
reason: this.normalizeReason(reason),
|
|
4241
|
-
// ws does not provide wasClean on Node-style close callback
|
|
4242
|
-
wasClean: code === 1e3
|
|
4243
|
-
});
|
|
4244
|
-
});
|
|
4251
|
+
let settled = false;
|
|
4252
|
+
const isCurrent = () => this.connectionEpoch === epoch && this.ws === socket;
|
|
4253
|
+
const finish = (error) => {
|
|
4254
|
+
if (settled) return;
|
|
4255
|
+
settled = true;
|
|
4256
|
+
if (this.cancelConnectAttempt === handleAbort) {
|
|
4257
|
+
this.cancelConnectAttempt = null;
|
|
4258
|
+
}
|
|
4259
|
+
signal?.removeEventListener("abort", handleAbort);
|
|
4260
|
+
if (error) {
|
|
4261
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
4245
4262
|
} else {
|
|
4246
|
-
|
|
4247
|
-
if (this.reconnectTimer) {
|
|
4248
|
-
clearTimeout(this.reconnectTimer);
|
|
4249
|
-
this.reconnectTimer = null;
|
|
4250
|
-
}
|
|
4251
|
-
this.reconnectAttempts = 0;
|
|
4252
|
-
this.emit("open", {});
|
|
4253
|
-
resolve();
|
|
4254
|
-
};
|
|
4255
|
-
this.ws.onmessage = (event) => {
|
|
4256
|
-
try {
|
|
4257
|
-
const data = event.data;
|
|
4258
|
-
const message = JSON.parse(data.toString());
|
|
4259
|
-
this.handleMessage(message);
|
|
4260
|
-
} catch (error) {
|
|
4261
|
-
console.error("[Granular] Failed to parse message:", error);
|
|
4262
|
-
}
|
|
4263
|
-
};
|
|
4264
|
-
this.ws.onerror = (event) => {
|
|
4265
|
-
const error = new Error("WebSocket error");
|
|
4266
|
-
error.event = event;
|
|
4267
|
-
this.emit("error", error);
|
|
4268
|
-
if (this.ws?.readyState !== READY_STATE_OPEN) {
|
|
4269
|
-
reject(error);
|
|
4270
|
-
}
|
|
4271
|
-
};
|
|
4272
|
-
this.ws.onclose = (event) => {
|
|
4273
|
-
this.handleDisconnect({
|
|
4274
|
-
code: event.code,
|
|
4275
|
-
reason: event.reason,
|
|
4276
|
-
wasClean: event.wasClean
|
|
4277
|
-
});
|
|
4278
|
-
};
|
|
4263
|
+
resolve();
|
|
4279
4264
|
}
|
|
4280
|
-
}
|
|
4281
|
-
|
|
4265
|
+
};
|
|
4266
|
+
const closeStaleSocket = () => {
|
|
4267
|
+
try {
|
|
4268
|
+
socket.close(1e3, "Stale connection attempt");
|
|
4269
|
+
} catch {
|
|
4270
|
+
}
|
|
4271
|
+
};
|
|
4272
|
+
const handleAbort = () => {
|
|
4273
|
+
if (isCurrent()) {
|
|
4274
|
+
this.connectionEpoch += 1;
|
|
4275
|
+
this.ws = null;
|
|
4276
|
+
}
|
|
4277
|
+
closeStaleSocket();
|
|
4278
|
+
finish(new Error("WebSocket connect aborted"));
|
|
4279
|
+
};
|
|
4280
|
+
this.cancelConnectAttempt = handleAbort;
|
|
4281
|
+
const handleOpen = () => {
|
|
4282
|
+
if (!isCurrent()) {
|
|
4283
|
+
closeStaleSocket();
|
|
4284
|
+
return;
|
|
4285
|
+
}
|
|
4286
|
+
this.reconnectAttempts = 0;
|
|
4287
|
+
this.emit("open", {});
|
|
4288
|
+
finish();
|
|
4289
|
+
};
|
|
4290
|
+
const handleMessage = (data) => {
|
|
4291
|
+
if (!isCurrent()) return;
|
|
4292
|
+
try {
|
|
4293
|
+
const text = typeof data === "string" ? data : data && typeof data === "object" && "toString" in data ? String(data.toString()) : "";
|
|
4294
|
+
this.handleMessage(JSON.parse(text));
|
|
4295
|
+
} catch (error) {
|
|
4296
|
+
console.error("[Granular] Failed to parse message:", error);
|
|
4297
|
+
}
|
|
4298
|
+
};
|
|
4299
|
+
const handleError = (error) => {
|
|
4300
|
+
if (!isCurrent()) return;
|
|
4301
|
+
const typedError = error instanceof Error ? error : new Error("WebSocket error");
|
|
4302
|
+
this.emit("error", typedError);
|
|
4303
|
+
if (socket.readyState !== READY_STATE_OPEN) finish(typedError);
|
|
4304
|
+
};
|
|
4305
|
+
const handleClose = (close) => {
|
|
4306
|
+
if (!isCurrent()) return;
|
|
4307
|
+
if (!settled) {
|
|
4308
|
+
finish(
|
|
4309
|
+
new Error(
|
|
4310
|
+
`WebSocket closed before ready${close.code ? ` (code=${close.code})` : ""}`
|
|
4311
|
+
)
|
|
4312
|
+
);
|
|
4313
|
+
}
|
|
4314
|
+
this.handleDisconnect({
|
|
4315
|
+
code: close.code,
|
|
4316
|
+
reason: this.normalizeReason(close.reason),
|
|
4317
|
+
wasClean: close.wasClean
|
|
4318
|
+
});
|
|
4319
|
+
};
|
|
4320
|
+
signal?.addEventListener("abort", handleAbort, { once: true });
|
|
4321
|
+
const nodeSocket = socket;
|
|
4322
|
+
if (typeof nodeSocket.on === "function") {
|
|
4323
|
+
nodeSocket.on("open", handleOpen);
|
|
4324
|
+
nodeSocket.on("message", handleMessage);
|
|
4325
|
+
nodeSocket.on("error", handleError);
|
|
4326
|
+
nodeSocket.on(
|
|
4327
|
+
"close",
|
|
4328
|
+
(code, reason) => handleClose({ code, reason, wasClean: code === 1e3 })
|
|
4329
|
+
);
|
|
4330
|
+
} else {
|
|
4331
|
+
socket.onopen = handleOpen;
|
|
4332
|
+
socket.onmessage = (event) => handleMessage(event.data);
|
|
4333
|
+
socket.onerror = handleError;
|
|
4334
|
+
socket.onclose = (event) => handleClose(event);
|
|
4282
4335
|
}
|
|
4283
4336
|
});
|
|
4284
4337
|
}
|
|
@@ -4296,9 +4349,58 @@ var WSClient = class {
|
|
|
4296
4349
|
return void 0;
|
|
4297
4350
|
}
|
|
4298
4351
|
rejectPending(error) {
|
|
4299
|
-
this.messageQueue.forEach((pending) =>
|
|
4352
|
+
this.messageQueue.forEach((pending) => {
|
|
4353
|
+
clearTimeout(pending.timeout);
|
|
4354
|
+
pending.reject(error);
|
|
4355
|
+
});
|
|
4300
4356
|
this.messageQueue = [];
|
|
4301
4357
|
}
|
|
4358
|
+
emitReconnectErrorMessage(error) {
|
|
4359
|
+
const reconnectInfo = {
|
|
4360
|
+
error,
|
|
4361
|
+
sessionId: this.sessionId,
|
|
4362
|
+
timestamp: Date.now()
|
|
4363
|
+
};
|
|
4364
|
+
this.emit("reconnect_error", reconnectInfo);
|
|
4365
|
+
if (this.options.onReconnectError) {
|
|
4366
|
+
try {
|
|
4367
|
+
this.options.onReconnectError(reconnectInfo);
|
|
4368
|
+
} catch (callbackError) {
|
|
4369
|
+
console.error(
|
|
4370
|
+
"[Granular] onReconnectError callback failed:",
|
|
4371
|
+
callbackError
|
|
4372
|
+
);
|
|
4373
|
+
}
|
|
4374
|
+
}
|
|
4375
|
+
}
|
|
4376
|
+
scheduleReconnectAttempt() {
|
|
4377
|
+
if (this.isExplicitlyDisconnected || this.reconnectTimer) return null;
|
|
4378
|
+
const baseReconnectDelayMs = typeof this.options.reconnectDelayMs === "number" && Number.isFinite(this.options.reconnectDelayMs) && this.options.reconnectDelayMs > 0 ? this.options.reconnectDelayMs : DEFAULT_RECONNECT_DELAY_MS;
|
|
4379
|
+
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;
|
|
4380
|
+
if (this.reconnectAttempts >= maxReconnectAttempts) {
|
|
4381
|
+
this.emitReconnectErrorMessage(
|
|
4382
|
+
`WebSocket reconnect attempts exhausted after ${maxReconnectAttempts} attempt(s).`
|
|
4383
|
+
);
|
|
4384
|
+
return null;
|
|
4385
|
+
}
|
|
4386
|
+
this.reconnectAttempts += 1;
|
|
4387
|
+
const reconnectDelayMs = Math.min(
|
|
4388
|
+
3e4,
|
|
4389
|
+
baseReconnectDelayMs * 2 ** Math.max(0, this.reconnectAttempts - 1)
|
|
4390
|
+
);
|
|
4391
|
+
this.reconnectTimer = setTimeout(() => {
|
|
4392
|
+
this.reconnectTimer = null;
|
|
4393
|
+
console.log("[Granular] Attempting reconnect...");
|
|
4394
|
+
this.connect().catch((error) => {
|
|
4395
|
+
console.error("[Granular] Reconnect failed:", error);
|
|
4396
|
+
this.emitReconnectErrorMessage(
|
|
4397
|
+
error instanceof Error ? error.message : String(error)
|
|
4398
|
+
);
|
|
4399
|
+
this.scheduleReconnectAttempt();
|
|
4400
|
+
});
|
|
4401
|
+
}, reconnectDelayMs);
|
|
4402
|
+
return reconnectDelayMs;
|
|
4403
|
+
}
|
|
4302
4404
|
buildDisconnectError(info) {
|
|
4303
4405
|
const details = [
|
|
4304
4406
|
info.code !== void 0 ? `code=${info.code}` : void 0,
|
|
@@ -4308,8 +4410,6 @@ var WSClient = class {
|
|
|
4308
4410
|
return new Error(`WebSocket disconnected${suffix}`);
|
|
4309
4411
|
}
|
|
4310
4412
|
handleDisconnect(close = {}) {
|
|
4311
|
-
const baseReconnectDelayMs = typeof this.options.reconnectDelayMs === "number" && Number.isFinite(this.options.reconnectDelayMs) && this.options.reconnectDelayMs > 0 ? this.options.reconnectDelayMs : DEFAULT_RECONNECT_DELAY_MS;
|
|
4312
|
-
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;
|
|
4313
4413
|
const unexpected = !this.isExplicitlyDisconnected;
|
|
4314
4414
|
const info = {
|
|
4315
4415
|
code: close.code,
|
|
@@ -4329,32 +4429,9 @@ var WSClient = class {
|
|
|
4329
4429
|
const disconnectError = this.buildDisconnectError(info);
|
|
4330
4430
|
this.rejectPending(disconnectError);
|
|
4331
4431
|
this.emit("disconnect", info);
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
sessionId: this.sessionId,
|
|
4336
|
-
timestamp: Date.now()
|
|
4337
|
-
};
|
|
4338
|
-
this.emit("reconnect_error", reconnectInfo);
|
|
4339
|
-
if (this.options.onReconnectError) {
|
|
4340
|
-
try {
|
|
4341
|
-
this.options.onReconnectError(reconnectInfo);
|
|
4342
|
-
} catch (callbackError) {
|
|
4343
|
-
console.error(
|
|
4344
|
-
"[Granular] onReconnectError callback failed:",
|
|
4345
|
-
callbackError
|
|
4346
|
-
);
|
|
4347
|
-
}
|
|
4348
|
-
}
|
|
4349
|
-
return;
|
|
4350
|
-
}
|
|
4351
|
-
this.reconnectAttempts += 1;
|
|
4352
|
-
const reconnectDelayMs = Math.min(
|
|
4353
|
-
3e4,
|
|
4354
|
-
baseReconnectDelayMs * 2 ** Math.max(0, this.reconnectAttempts - 1)
|
|
4355
|
-
);
|
|
4356
|
-
info.reconnectScheduled = true;
|
|
4357
|
-
info.reconnectDelayMs = reconnectDelayMs;
|
|
4432
|
+
const reconnectDelayMs = this.scheduleReconnectAttempt();
|
|
4433
|
+
info.reconnectScheduled = reconnectDelayMs !== null;
|
|
4434
|
+
if (reconnectDelayMs !== null) info.reconnectDelayMs = reconnectDelayMs;
|
|
4358
4435
|
if (this.options.onUnexpectedClose) {
|
|
4359
4436
|
try {
|
|
4360
4437
|
this.options.onUnexpectedClose(info);
|
|
@@ -4365,28 +4442,6 @@ var WSClient = class {
|
|
|
4365
4442
|
);
|
|
4366
4443
|
}
|
|
4367
4444
|
}
|
|
4368
|
-
this.reconnectTimer = setTimeout(() => {
|
|
4369
|
-
console.log("[Granular] Attempting reconnect...");
|
|
4370
|
-
this.connect().catch((error) => {
|
|
4371
|
-
console.error("[Granular] Reconnect failed:", error);
|
|
4372
|
-
const reconnectInfo = {
|
|
4373
|
-
error: error instanceof Error ? error.message : String(error),
|
|
4374
|
-
sessionId: this.sessionId,
|
|
4375
|
-
timestamp: Date.now()
|
|
4376
|
-
};
|
|
4377
|
-
this.emit("reconnect_error", reconnectInfo);
|
|
4378
|
-
if (this.options.onReconnectError) {
|
|
4379
|
-
try {
|
|
4380
|
-
this.options.onReconnectError(reconnectInfo);
|
|
4381
|
-
} catch (callbackError) {
|
|
4382
|
-
console.error(
|
|
4383
|
-
"[Granular] onReconnectError callback failed:",
|
|
4384
|
-
callbackError
|
|
4385
|
-
);
|
|
4386
|
-
}
|
|
4387
|
-
}
|
|
4388
|
-
});
|
|
4389
|
-
}, reconnectDelayMs);
|
|
4390
4445
|
}
|
|
4391
4446
|
}
|
|
4392
4447
|
handleMessage(message) {
|
|
@@ -4497,6 +4552,7 @@ var WSClient = class {
|
|
|
4497
4552
|
const response = message;
|
|
4498
4553
|
const pending = this.messageQueue.find((q) => q.id === response.id);
|
|
4499
4554
|
if (pending) {
|
|
4555
|
+
clearTimeout(pending.timeout);
|
|
4500
4556
|
if (response.type === "rpc_error") {
|
|
4501
4557
|
pending.reject(
|
|
4502
4558
|
new Error(
|
|
@@ -4542,16 +4598,22 @@ var WSClient = class {
|
|
|
4542
4598
|
id
|
|
4543
4599
|
};
|
|
4544
4600
|
return new Promise((resolve, reject) => {
|
|
4545
|
-
this.messageQueue.push({ resolve, reject, id });
|
|
4546
|
-
this.ws.send(JSON.stringify(request));
|
|
4547
4601
|
const timeoutMs = rpcTimeoutMsForMethod(method);
|
|
4548
|
-
setTimeout(() => {
|
|
4602
|
+
const timeout = setTimeout(() => {
|
|
4549
4603
|
const pending = this.messageQueue.find((q) => q.id === id);
|
|
4550
4604
|
if (pending) {
|
|
4551
4605
|
this.messageQueue = this.messageQueue.filter((q) => q.id !== id);
|
|
4552
4606
|
reject(new Error(`RPC timeout: ${method}`));
|
|
4553
4607
|
}
|
|
4554
4608
|
}, timeoutMs);
|
|
4609
|
+
this.messageQueue.push({ resolve, reject, id, timeout });
|
|
4610
|
+
try {
|
|
4611
|
+
this.ws.send(JSON.stringify(request));
|
|
4612
|
+
} catch (error) {
|
|
4613
|
+
clearTimeout(timeout);
|
|
4614
|
+
this.messageQueue = this.messageQueue.filter((q) => q.id !== id);
|
|
4615
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
4616
|
+
}
|
|
4555
4617
|
});
|
|
4556
4618
|
}
|
|
4557
4619
|
async handleIncomingRpc(request) {
|
|
@@ -4637,15 +4699,18 @@ var WSClient = class {
|
|
|
4637
4699
|
/**
|
|
4638
4700
|
* Disconnect the WebSocket and clear state
|
|
4639
4701
|
*/
|
|
4640
|
-
disconnect() {
|
|
4702
|
+
disconnect(options = {}) {
|
|
4641
4703
|
this.isExplicitlyDisconnected = true;
|
|
4704
|
+
this.cancelConnectAttempt?.();
|
|
4705
|
+
this.cancelConnectAttempt = null;
|
|
4706
|
+
this.connectionEpoch += 1;
|
|
4642
4707
|
if (this.reconnectTimer) {
|
|
4643
4708
|
clearTimeout(this.reconnectTimer);
|
|
4644
4709
|
this.reconnectTimer = null;
|
|
4645
4710
|
}
|
|
4646
4711
|
this.clearTokenRefreshTimer();
|
|
4647
4712
|
if (this.ws) {
|
|
4648
|
-
this.ws.close(1e3, "Client disconnect");
|
|
4713
|
+
this.ws.close(1e3, options.reason || "Client disconnect");
|
|
4649
4714
|
this.ws = null;
|
|
4650
4715
|
}
|
|
4651
4716
|
this.rejectPending(new Error("Client explicitly disconnected"));
|
|
@@ -4741,8 +4806,12 @@ function normalizePrompt(rawValue) {
|
|
|
4741
4806
|
const source = promptRecord || raw;
|
|
4742
4807
|
const id = typeof source.id === "string" ? source.id : typeof raw.id === "string" ? raw.id : typeof raw.promptId === "string" ? raw.promptId : "";
|
|
4743
4808
|
if (!id) return null;
|
|
4809
|
+
const jobId = typeof source.jobId === "string" && source.jobId.trim() ? source.jobId.trim() : typeof raw.jobId === "string" && raw.jobId.trim() ? raw.jobId.trim() : void 0;
|
|
4810
|
+
const turnId = typeof source.turnId === "string" && source.turnId.trim() ? source.turnId.trim() : typeof raw.turnId === "string" && raw.turnId.trim() ? raw.turnId.trim() : void 0;
|
|
4744
4811
|
return {
|
|
4745
4812
|
id,
|
|
4813
|
+
...jobId ? { jobId } : {},
|
|
4814
|
+
...turnId ? { turnId } : {},
|
|
4746
4815
|
type: normalizePromptType(source === raw ? raw : { ...raw, ...source }),
|
|
4747
4816
|
title: typeof source.title === "string" ? source.title : "Input required",
|
|
4748
4817
|
message: typeof source.message === "string" ? source.message : "",
|
|
@@ -4783,6 +4852,9 @@ function resolvePromptAnswer(prompt, answer) {
|
|
|
4783
4852
|
|
|
4784
4853
|
// src/session.ts
|
|
4785
4854
|
var PROMPT_TRANSCRIPT_APPEND_TIMEOUT_MS = 5e3;
|
|
4855
|
+
function toPascalCase(value) {
|
|
4856
|
+
return value.split(/[_:\-\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
4857
|
+
}
|
|
4786
4858
|
function withPromptTranscriptTimeout(promise) {
|
|
4787
4859
|
let timeout = null;
|
|
4788
4860
|
return Promise.race([
|
|
@@ -4826,6 +4898,9 @@ var Session = class {
|
|
|
4826
4898
|
this.initialQuota = options.initialQuota || null;
|
|
4827
4899
|
this.setupEventHandlers();
|
|
4828
4900
|
this.setupToolInvokeHandler();
|
|
4901
|
+
this.currentDomainRevision = this.extractDomainRevisionFromDoc(
|
|
4902
|
+
this.client.doc
|
|
4903
|
+
);
|
|
4829
4904
|
}
|
|
4830
4905
|
extractDomainRevisionFromDoc(doc) {
|
|
4831
4906
|
const domain = doc?.domain;
|
|
@@ -5345,9 +5420,7 @@ var Session = class {
|
|
|
5345
5420
|
if (classes && Object.keys(classes).length > 0) {
|
|
5346
5421
|
let docs2 = "# Domain Documentation\n\n";
|
|
5347
5422
|
docs2 += "Import concrete classes from `@granular/domain/<Class>` and global backend actions from `@granular/actions/backend`:\n\n";
|
|
5348
|
-
const classNames = Object.keys(classes).map(
|
|
5349
|
-
(c) => c.charAt(0).toUpperCase() + c.slice(1)
|
|
5350
|
-
);
|
|
5423
|
+
const classNames = Object.keys(classes).map(toPascalCase);
|
|
5351
5424
|
const globalNames = (globalTools || []).map((t) => t.name);
|
|
5352
5425
|
const importLines = [
|
|
5353
5426
|
...classNames.map(
|
|
@@ -5361,7 +5434,7 @@ ${importLines.join("\n") || "// No generated domain imports available."}
|
|
|
5361
5434
|
|
|
5362
5435
|
`;
|
|
5363
5436
|
for (const [className, cls] of Object.entries(classes)) {
|
|
5364
|
-
const TsName =
|
|
5437
|
+
const TsName = toPascalCase(className);
|
|
5365
5438
|
docs2 += `## ${TsName}
|
|
5366
5439
|
|
|
5367
5440
|
`;
|
|
@@ -5733,6 +5806,7 @@ function normalizeJobAgentMessageEnvelope(data) {
|
|
|
5733
5806
|
}
|
|
5734
5807
|
return {
|
|
5735
5808
|
jobId: d.jobId,
|
|
5809
|
+
...typeof d.turnId === "string" && d.turnId.trim() ? { turnId: d.turnId.trim() } : {},
|
|
5736
5810
|
message: {
|
|
5737
5811
|
messageId: d.messageId,
|
|
5738
5812
|
kind: d.kind === "artifacts" ? "artifacts" : "text",
|
|
@@ -6351,6 +6425,28 @@ function asString(value) {
|
|
|
6351
6425
|
function trimString(value) {
|
|
6352
6426
|
return typeof value === "string" ? value.trim() : "";
|
|
6353
6427
|
}
|
|
6428
|
+
function compactJson(value, maxLength = 320) {
|
|
6429
|
+
if (value === void 0 || value === null) return void 0;
|
|
6430
|
+
try {
|
|
6431
|
+
const json = JSON.stringify(value);
|
|
6432
|
+
if (!json || json === "undefined") return void 0;
|
|
6433
|
+
return json.length > maxLength ? `${json.slice(0, maxLength)}...` : json;
|
|
6434
|
+
} catch {
|
|
6435
|
+
return String(value);
|
|
6436
|
+
}
|
|
6437
|
+
}
|
|
6438
|
+
function artifactRecordsById(liveDoc) {
|
|
6439
|
+
const artifacts = asRecord3(liveDoc?.artifacts);
|
|
6440
|
+
const byId = asRecord3(artifacts?.byId) || {};
|
|
6441
|
+
return Object.fromEntries(
|
|
6442
|
+
Object.entries(byId).map(([artifactId, value]) => {
|
|
6443
|
+
const record = asRecord3(value);
|
|
6444
|
+
return record ? [artifactId, record] : null;
|
|
6445
|
+
}).filter(
|
|
6446
|
+
(entry) => Boolean(entry)
|
|
6447
|
+
)
|
|
6448
|
+
);
|
|
6449
|
+
}
|
|
6354
6450
|
function normalizeShowRefs(value) {
|
|
6355
6451
|
const record = asRecord3(value);
|
|
6356
6452
|
if (!record) return void 0;
|
|
@@ -6367,9 +6463,106 @@ function normalizeShowRefs(value) {
|
|
|
6367
6463
|
entryPaths: normalizeRefs(record.entryPaths),
|
|
6368
6464
|
listNames: normalizeRefs(record.listNames),
|
|
6369
6465
|
variableNames: normalizeRefs(record.variableNames),
|
|
6370
|
-
fileIds: normalizeRefs(record.fileIds)
|
|
6466
|
+
fileIds: normalizeRefs(record.fileIds),
|
|
6467
|
+
sessionArtifactIds: normalizeRefs(record.sessionArtifactIds),
|
|
6468
|
+
actionSuggestions: normalizeActionSuggestions(record.actionSuggestions),
|
|
6469
|
+
tables: Array.isArray(record.tables) ? record.tables.filter(
|
|
6470
|
+
(table) => Boolean(
|
|
6471
|
+
table && typeof table === "object" && !Array.isArray(table) && Array.isArray(table.columns) && Array.isArray(table.rows)
|
|
6472
|
+
)
|
|
6473
|
+
) : void 0
|
|
6371
6474
|
};
|
|
6372
|
-
return show.entryPaths || show.listNames || show.variableNames || show.fileIds ? show : void 0;
|
|
6475
|
+
return show.entryPaths || show.listNames || show.variableNames || show.fileIds || show.sessionArtifactIds || show.actionSuggestions || show.tables ? show : void 0;
|
|
6476
|
+
}
|
|
6477
|
+
function normalizeActionSuggestions(value) {
|
|
6478
|
+
if (!Array.isArray(value)) return void 0;
|
|
6479
|
+
const suggestions = [];
|
|
6480
|
+
for (const item of value) {
|
|
6481
|
+
const record = asRecord3(item);
|
|
6482
|
+
if (!record) continue;
|
|
6483
|
+
const label = trimString(record.label);
|
|
6484
|
+
if (!label) continue;
|
|
6485
|
+
const suggestionId = trimString(record.suggestionId) || trimString(record.id) || label;
|
|
6486
|
+
suggestions.push({
|
|
6487
|
+
suggestionId,
|
|
6488
|
+
label,
|
|
6489
|
+
...typeof record.description === "string" ? { description: record.description } : {},
|
|
6490
|
+
...asRecord3(record.artifact) ? { artifact: asRecord3(record.artifact) } : {},
|
|
6491
|
+
...asRecord3(record.target) ? { target: asRecord3(record.target) } : {},
|
|
6492
|
+
...asRecord3(record.metadata) ? { metadata: asRecord3(record.metadata) } : {}
|
|
6493
|
+
});
|
|
6494
|
+
}
|
|
6495
|
+
return suggestions.length ? suggestions : void 0;
|
|
6496
|
+
}
|
|
6497
|
+
var TRANSCRIPT_MESSAGE_PART_LIMIT = 128;
|
|
6498
|
+
var TRANSCRIPT_MESSAGE_PART_TEXT_LIMIT = 2e5;
|
|
6499
|
+
var TRANSCRIPT_MESSAGE_PART_ACTION_LABEL_LIMIT = 1e3;
|
|
6500
|
+
var TRANSCRIPT_MESSAGE_ACTION_LIMIT = 64;
|
|
6501
|
+
function normalizeConversationMessageActions(value) {
|
|
6502
|
+
if (!Array.isArray(value) || value.length === 0) return void 0;
|
|
6503
|
+
const actions = [];
|
|
6504
|
+
for (const item of value.slice(0, TRANSCRIPT_MESSAGE_ACTION_LIMIT)) {
|
|
6505
|
+
const record = asRecord3(item);
|
|
6506
|
+
const kind = record?.kind;
|
|
6507
|
+
const label = trimString(record?.label ?? record?.title);
|
|
6508
|
+
const status = record?.status;
|
|
6509
|
+
if (kind !== "frontend" && kind !== "backend" && kind !== "system" || !label || label.length > TRANSCRIPT_MESSAGE_PART_ACTION_LABEL_LIMIT) {
|
|
6510
|
+
continue;
|
|
6511
|
+
}
|
|
6512
|
+
actions.push({
|
|
6513
|
+
kind,
|
|
6514
|
+
label,
|
|
6515
|
+
...status === "done" || status === "queued" || status === "failed" ? { status } : {}
|
|
6516
|
+
});
|
|
6517
|
+
}
|
|
6518
|
+
return actions.length ? actions : void 0;
|
|
6519
|
+
}
|
|
6520
|
+
function normalizeConversationMessageParts(value, canonicalContent, canonicalActions) {
|
|
6521
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > TRANSCRIPT_MESSAGE_PART_LIMIT) {
|
|
6522
|
+
return void 0;
|
|
6523
|
+
}
|
|
6524
|
+
const parts = [];
|
|
6525
|
+
const canonicalActionsById = new Map(
|
|
6526
|
+
(canonicalActions || []).map((action) => [
|
|
6527
|
+
`${action.kind}:${action.label}`,
|
|
6528
|
+
action
|
|
6529
|
+
])
|
|
6530
|
+
);
|
|
6531
|
+
const seenActionIds = /* @__PURE__ */ new Set();
|
|
6532
|
+
let textLength = 0;
|
|
6533
|
+
for (const item of value) {
|
|
6534
|
+
const record = asRecord3(item);
|
|
6535
|
+
if (!record) return void 0;
|
|
6536
|
+
if (record.type === "text") {
|
|
6537
|
+
if (typeof record.text !== "string" || record.text.length === 0) {
|
|
6538
|
+
return void 0;
|
|
6539
|
+
}
|
|
6540
|
+
textLength += record.text.length;
|
|
6541
|
+
if (textLength > TRANSCRIPT_MESSAGE_PART_TEXT_LIMIT) return void 0;
|
|
6542
|
+
parts.push({ type: "text", text: record.text });
|
|
6543
|
+
continue;
|
|
6544
|
+
}
|
|
6545
|
+
if (record.type !== "action") return void 0;
|
|
6546
|
+
const action = asRecord3(record.action);
|
|
6547
|
+
const kind = action?.kind;
|
|
6548
|
+
const label = trimString(action?.label);
|
|
6549
|
+
if (kind !== "frontend" && kind !== "backend" && kind !== "system" || !label || label.length > TRANSCRIPT_MESSAGE_PART_ACTION_LABEL_LIMIT) {
|
|
6550
|
+
return void 0;
|
|
6551
|
+
}
|
|
6552
|
+
const actionId = `${kind}:${label}`;
|
|
6553
|
+
const canonicalAction = canonicalActionsById.get(actionId);
|
|
6554
|
+
if (!canonicalAction) return void 0;
|
|
6555
|
+
if (seenActionIds.has(actionId)) continue;
|
|
6556
|
+
seenActionIds.add(actionId);
|
|
6557
|
+
parts.push({
|
|
6558
|
+
type: "action",
|
|
6559
|
+
action: canonicalAction
|
|
6560
|
+
});
|
|
6561
|
+
}
|
|
6562
|
+
const orderedText = parts.filter(
|
|
6563
|
+
(part) => part.type === "text"
|
|
6564
|
+
).map((part) => part.text).join("");
|
|
6565
|
+
return orderedText === canonicalContent ? parts : void 0;
|
|
6373
6566
|
}
|
|
6374
6567
|
function stringifyTranscriptValue(value, fallback = "") {
|
|
6375
6568
|
if (typeof value === "string") {
|
|
@@ -6389,12 +6582,139 @@ function stringifyTranscriptValue(value, fallback = "") {
|
|
|
6389
6582
|
return String(value);
|
|
6390
6583
|
}
|
|
6391
6584
|
}
|
|
6392
|
-
function
|
|
6585
|
+
function latestInputEditSummary(metadata) {
|
|
6586
|
+
const lastInputEdit = asRecord3(metadata.lastInputEdit);
|
|
6587
|
+
if (!lastInputEdit) return null;
|
|
6588
|
+
const source = asString(lastInputEdit.source) || "unknown";
|
|
6589
|
+
const actor = asString(lastInputEdit.actorSubjectId) || asString(lastInputEdit.actorPermissionProfileName) || asString(lastInputEdit.jobId) || null;
|
|
6590
|
+
const inputKeys = Array.isArray(lastInputEdit.changedInputKeys) ? lastInputEdit.changedInputKeys.filter(
|
|
6591
|
+
(key) => typeof key === "string" && key.trim().length > 0
|
|
6592
|
+
).slice(0, 6) : [];
|
|
6593
|
+
const relationshipKeys = Array.isArray(lastInputEdit.changedRelationshipKeys) ? lastInputEdit.changedRelationshipKeys.filter(
|
|
6594
|
+
(key) => typeof key === "string" && key.trim().length > 0
|
|
6595
|
+
).slice(0, 6) : [];
|
|
6596
|
+
const changed = [
|
|
6597
|
+
inputKeys.length ? `inputs=${inputKeys.join(",")}` : null,
|
|
6598
|
+
relationshipKeys.length ? `relationships=${relationshipKeys.join(",")}` : null
|
|
6599
|
+
].filter(Boolean);
|
|
6600
|
+
return `lastEdit=${source}${actor ? ` by ${actor}` : ""}${changed.length ? ` (${changed.join("; ")})` : ""}`;
|
|
6601
|
+
}
|
|
6602
|
+
function artifactIssueSummary(record) {
|
|
6603
|
+
const validation = asRecord3(record.validation);
|
|
6604
|
+
if (!validation) return null;
|
|
6605
|
+
const issues = Array.isArray(validation.issues) ? validation.issues.map((issue) => asRecord3(issue)).filter((issue) => Boolean(issue)).slice(0, 3) : [];
|
|
6606
|
+
if (issues.length > 0) {
|
|
6607
|
+
return `issues=${issues.map((issue) => {
|
|
6608
|
+
const code = asString(issue.code) || asString(issue.kind) || "issue";
|
|
6609
|
+
const path = asString(issue.path);
|
|
6610
|
+
const message = trimString(issue.message);
|
|
6611
|
+
return `${code}${path ? ` at ${path}` : ""}${message ? ` (${message})` : ""}`;
|
|
6612
|
+
}).join("; ")}`;
|
|
6613
|
+
}
|
|
6614
|
+
const error = trimString(validation.error) || trimString(validation.reason) || trimString(validation.message);
|
|
6615
|
+
return error ? `validation=${error}` : null;
|
|
6616
|
+
}
|
|
6617
|
+
function artifactExecutionSummary(metadata) {
|
|
6618
|
+
const execution = asRecord3(metadata.execution);
|
|
6619
|
+
if (!execution) return null;
|
|
6620
|
+
const result = asRecord3(execution.result);
|
|
6621
|
+
const awaiting = asString(result?.awaiting) || asString(execution.awaiting);
|
|
6622
|
+
const pendingTransition = asString(result?.pendingTransition) || asString(execution.pendingTransition);
|
|
6623
|
+
const approval = asRecord3(result?.approval) || asRecord3(execution.approval);
|
|
6624
|
+
const approvalTarget = asString(approval?.permissionProfileName) || asString(approval?.permissionProfileId) || asString(approval?.assigneeSubjectId);
|
|
6625
|
+
const error = trimString(execution.error);
|
|
6626
|
+
const pieces = [
|
|
6627
|
+
awaiting ? `awaiting=${awaiting}` : null,
|
|
6628
|
+
pendingTransition ? `pendingTransition=${pendingTransition}` : null,
|
|
6629
|
+
approvalTarget ? `approvalTarget=${approvalTarget}` : null,
|
|
6630
|
+
error ? `executionError=${error}` : null
|
|
6631
|
+
].filter(Boolean);
|
|
6632
|
+
return pieces.length ? pieces.join("; ") : null;
|
|
6633
|
+
}
|
|
6634
|
+
function artifactStatePathSummary(metadata) {
|
|
6635
|
+
const statePlan = asRecord3(metadata.statePlan);
|
|
6636
|
+
if (!statePlan) return null;
|
|
6637
|
+
const machineName = asString(statePlan.machineName);
|
|
6638
|
+
const targetState = asString(statePlan.targetState);
|
|
6639
|
+
const objectPath = asString(statePlan.objectPath);
|
|
6640
|
+
const approvedTransitions = Array.isArray(statePlan.approvedTransitions) ? statePlan.approvedTransitions.length : 0;
|
|
6641
|
+
const approvalDecisions = Array.isArray(statePlan.approvalDecisions) ? statePlan.approvalDecisions.length : 0;
|
|
6642
|
+
const pieces = [
|
|
6643
|
+
machineName || targetState ? `statePath=${machineName || "state_machine"}${targetState ? ` -> ${targetState}` : ""}` : null,
|
|
6644
|
+
objectPath ? `objectPath=${objectPath}` : null,
|
|
6645
|
+
approvedTransitions ? `approvedTransitions=${approvedTransitions}` : null,
|
|
6646
|
+
approvalDecisions ? `approvalDecisions=${approvalDecisions}` : null
|
|
6647
|
+
].filter(Boolean);
|
|
6648
|
+
return pieces.length ? pieces.join("; ") : null;
|
|
6649
|
+
}
|
|
6650
|
+
function artifactSummaryLine(artifactId, record) {
|
|
6651
|
+
if (!record) return `- ${artifactId}: unavailable in session artifact store`;
|
|
6652
|
+
const label = trimString(record.label) || artifactId;
|
|
6653
|
+
const kind = asString(record.kind) || "artifact";
|
|
6654
|
+
const status = asString(record.status) || "unknown";
|
|
6655
|
+
const createdByJobId = asString(record.createdByJobId);
|
|
6656
|
+
const target = asRecord3(record.target);
|
|
6657
|
+
const metadata = asRecord3(record.metadata) || {};
|
|
6658
|
+
const subArtifactIds = Array.isArray(record.subArtifactIds) ? record.subArtifactIds.filter(
|
|
6659
|
+
(id) => typeof id === "string" && id.trim().length > 0
|
|
6660
|
+
).slice(0, 8) : [];
|
|
6661
|
+
const relationships = compactJson(record.relationships, 220);
|
|
6662
|
+
const pieces = [
|
|
6663
|
+
`kind=${kind}`,
|
|
6664
|
+
`status=${status}`,
|
|
6665
|
+
createdByJobId ? `createdByJob=${createdByJobId}` : null,
|
|
6666
|
+
target ? `target=${asString(target.className) || "record"}:${asString(target.id) || "unknown"}${asString(target.label) ? ` (${asString(target.label)})` : ""}` : null,
|
|
6667
|
+
artifactStatePathSummary(metadata),
|
|
6668
|
+
artifactExecutionSummary(metadata),
|
|
6669
|
+
artifactIssueSummary(record),
|
|
6670
|
+
latestInputEditSummary(metadata),
|
|
6671
|
+
subArtifactIds.length ? `subArtifacts=${subArtifactIds.join(",")}` : null,
|
|
6672
|
+
relationships ? `relationships=${relationships}` : null
|
|
6673
|
+
].filter(Boolean);
|
|
6674
|
+
return `- ${artifactId}: ${label}${pieces.length ? `; ${pieces.join("; ")}` : ""}`;
|
|
6675
|
+
}
|
|
6676
|
+
function buildArtifactHistory(show, artifactsById) {
|
|
6393
6677
|
if (!show) return void 0;
|
|
6394
|
-
|
|
6678
|
+
const artifactIds = show.sessionArtifactIds || [];
|
|
6679
|
+
const actionSuggestions = show.actionSuggestions || [];
|
|
6680
|
+
if (artifactIds.length === 0 && actionSuggestions.length === 0) {
|
|
6681
|
+
return `[Agent message]
|
|
6395
6682
|
${stringifyTranscriptValue({ show }, "")}`;
|
|
6683
|
+
}
|
|
6684
|
+
const lines = artifactIds.slice(0, 8).map(
|
|
6685
|
+
(artifactId) => artifactSummaryLine(artifactId, artifactsById?.[artifactId])
|
|
6686
|
+
);
|
|
6687
|
+
if (artifactIds.length > 8) {
|
|
6688
|
+
lines.push(`- ${artifactIds.length - 8} more artifacts omitted`);
|
|
6689
|
+
}
|
|
6690
|
+
if (actionSuggestions.length > 0) {
|
|
6691
|
+
if (artifactIds.length > 0) lines.push("[Agent suggested actions]");
|
|
6692
|
+
for (const suggestion of actionSuggestions.slice(0, 8)) {
|
|
6693
|
+
lines.push(
|
|
6694
|
+
`- ${suggestion.label}${suggestion.description ? `; ${suggestion.description}` : ""}`
|
|
6695
|
+
);
|
|
6696
|
+
}
|
|
6697
|
+
if (actionSuggestions.length > 8) {
|
|
6698
|
+
lines.push(`- ${actionSuggestions.length - 8} more suggestions omitted`);
|
|
6699
|
+
}
|
|
6700
|
+
}
|
|
6701
|
+
const otherRefs = {
|
|
6702
|
+
entryPaths: show.entryPaths,
|
|
6703
|
+
listNames: show.listNames,
|
|
6704
|
+
variableNames: show.variableNames,
|
|
6705
|
+
fileIds: show.fileIds
|
|
6706
|
+
};
|
|
6707
|
+
const hasOtherRefs = Object.values(otherRefs).some(
|
|
6708
|
+
(value) => Array.isArray(value) && value.length > 0
|
|
6709
|
+
);
|
|
6710
|
+
const title = artifactIds.length > 0 ? "[Agent displayed session artifacts]" : "[Agent suggested actions]";
|
|
6711
|
+
return [
|
|
6712
|
+
title,
|
|
6713
|
+
...lines,
|
|
6714
|
+
hasOtherRefs ? `Other shown refs: ${stringifyTranscriptValue(otherRefs, "")}` : null
|
|
6715
|
+
].filter(Boolean).join("\n");
|
|
6396
6716
|
}
|
|
6397
|
-
function normalizeConversationMessage(raw) {
|
|
6717
|
+
function normalizeConversationMessage(raw, artifactsById) {
|
|
6398
6718
|
const record = asRecord3(raw);
|
|
6399
6719
|
if (!record) return null;
|
|
6400
6720
|
const role = record.role === "user" ? "user" : record.role === "assistant" ? "assistant" : null;
|
|
@@ -6402,10 +6722,18 @@ function normalizeConversationMessage(raw) {
|
|
|
6402
6722
|
const content = trimString(
|
|
6403
6723
|
record.content ?? record.reply ?? record.message ?? record.text
|
|
6404
6724
|
);
|
|
6725
|
+
const actions = role === "assistant" ? normalizeConversationMessageActions(record.actions) : void 0;
|
|
6726
|
+
const parts = role === "assistant" ? normalizeConversationMessageParts(record.parts, content, actions) : void 0;
|
|
6405
6727
|
const show = normalizeShowRefs(record.show);
|
|
6406
6728
|
const id = asString(record.id) || crypto.randomUUID();
|
|
6407
6729
|
const timestamp = asNumber(record.timestamp) || asNumber(record.ts) || 0;
|
|
6408
|
-
if (!content && !show) return null;
|
|
6730
|
+
if (!content && !show && !actions?.length) return null;
|
|
6731
|
+
const artifactHistory = buildArtifactHistory(show, artifactsById);
|
|
6732
|
+
const historyContent = role === "assistant" ? content && artifactHistory ? `[Assistant reply]
|
|
6733
|
+
${content}
|
|
6734
|
+
|
|
6735
|
+
${artifactHistory}` : content ? `[Assistant reply]
|
|
6736
|
+
${content}` : artifactHistory : void 0;
|
|
6409
6737
|
return {
|
|
6410
6738
|
id,
|
|
6411
6739
|
role,
|
|
@@ -6414,8 +6742,9 @@ function normalizeConversationMessage(raw) {
|
|
|
6414
6742
|
jobId: asString(record.jobId),
|
|
6415
6743
|
promptId: asString(record.promptId),
|
|
6416
6744
|
show,
|
|
6417
|
-
|
|
6418
|
-
|
|
6745
|
+
actions,
|
|
6746
|
+
parts,
|
|
6747
|
+
historyContent,
|
|
6419
6748
|
source: "conversation"
|
|
6420
6749
|
};
|
|
6421
6750
|
}
|
|
@@ -6458,7 +6787,7 @@ ${assistantContent}`,
|
|
|
6458
6787
|
return entries;
|
|
6459
6788
|
});
|
|
6460
6789
|
}
|
|
6461
|
-
function normalizeAgentMessageEntries(jobId, rawMessages) {
|
|
6790
|
+
function normalizeAgentMessageEntries(jobId, rawMessages, artifactsById) {
|
|
6462
6791
|
return asArray(rawMessages).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
|
|
6463
6792
|
(left, right) => (asNumber(left.timestamp) || asNumber(left.ts) || 0) - (asNumber(right.timestamp) || asNumber(right.ts) || 0)
|
|
6464
6793
|
).flatMap((message) => {
|
|
@@ -6489,14 +6818,14 @@ ${reply}`,
|
|
|
6489
6818
|
timestamp,
|
|
6490
6819
|
jobId,
|
|
6491
6820
|
show,
|
|
6492
|
-
historyContent: buildArtifactHistory(show),
|
|
6821
|
+
historyContent: buildArtifactHistory(show, artifactsById),
|
|
6493
6822
|
source: "job_agent_message"
|
|
6494
6823
|
});
|
|
6495
6824
|
}
|
|
6496
6825
|
return entries;
|
|
6497
6826
|
});
|
|
6498
6827
|
}
|
|
6499
|
-
function buildJobFallbackEntries(jobId, job, sessionHeap) {
|
|
6828
|
+
function buildJobFallbackEntries(jobId, job, sessionHeap, artifactsById) {
|
|
6500
6829
|
const timestamp = asNumber(job.finishedAt) || asNumber(job.startedAt) || asNumber(job.submittedAt) || 0;
|
|
6501
6830
|
const resultPreview = stringifyTranscriptValue(
|
|
6502
6831
|
job.result,
|
|
@@ -6534,7 +6863,7 @@ ${responseText}`,
|
|
|
6534
6863
|
timestamp,
|
|
6535
6864
|
jobId,
|
|
6536
6865
|
show,
|
|
6537
|
-
historyContent: buildArtifactHistory(show),
|
|
6866
|
+
historyContent: buildArtifactHistory(show, artifactsById),
|
|
6538
6867
|
source: "job_result"
|
|
6539
6868
|
});
|
|
6540
6869
|
}
|
|
@@ -6588,10 +6917,11 @@ function buildJobCodeEntry(jobId, job) {
|
|
|
6588
6917
|
function buildSessionTranscript(input) {
|
|
6589
6918
|
const liveDoc = input.liveDoc || null;
|
|
6590
6919
|
const sessionHeap = input.sessionHeap || EMPTY_HEAP;
|
|
6920
|
+
const artifactsById = artifactRecordsById(liveDoc);
|
|
6591
6921
|
const transcript = [];
|
|
6592
6922
|
const conversationMessages = asArray(
|
|
6593
6923
|
asRecord3(liveDoc?.conversation)?.messages
|
|
6594
|
-
).map((message) => normalizeConversationMessage(message)).filter((message) => Boolean(message));
|
|
6924
|
+
).map((message) => normalizeConversationMessage(message, artifactsById)).filter((message) => Boolean(message));
|
|
6595
6925
|
const conversationPromptIds = new Set(
|
|
6596
6926
|
conversationMessages.map((message) => message.promptId).filter((promptId) => Boolean(promptId))
|
|
6597
6927
|
);
|
|
@@ -6618,7 +6948,8 @@ function buildSessionTranscript(input) {
|
|
|
6618
6948
|
if (!assistantConversationJobIds.has(jobId)) {
|
|
6619
6949
|
const agentEntries = normalizeAgentMessageEntries(
|
|
6620
6950
|
jobId,
|
|
6621
|
-
job.agentMessages
|
|
6951
|
+
job.agentMessages,
|
|
6952
|
+
artifactsById
|
|
6622
6953
|
);
|
|
6623
6954
|
if (agentEntries.length > 0) {
|
|
6624
6955
|
transcript.push(...agentEntries);
|
|
@@ -6627,7 +6958,8 @@ function buildSessionTranscript(input) {
|
|
|
6627
6958
|
...buildJobFallbackEntries(
|
|
6628
6959
|
jobId,
|
|
6629
6960
|
job,
|
|
6630
|
-
sessionHeap
|
|
6961
|
+
sessionHeap,
|
|
6962
|
+
artifactsById
|
|
6631
6963
|
)
|
|
6632
6964
|
);
|
|
6633
6965
|
}
|
|
@@ -10793,16 +11125,107 @@ var StateMachineStateSchema = external_exports.union([
|
|
|
10793
11125
|
external_exports.string(),
|
|
10794
11126
|
external_exports.object({
|
|
10795
11127
|
name: external_exports.string().min(1),
|
|
11128
|
+
label: external_exports.string().optional(),
|
|
11129
|
+
description: external_exports.string().optional(),
|
|
10796
11130
|
isFinal: external_exports.boolean().optional()
|
|
10797
11131
|
}).strict()
|
|
10798
11132
|
]);
|
|
11133
|
+
var StateTransitionInputBindingSchema = external_exports.lazy(
|
|
11134
|
+
() => external_exports.union([
|
|
11135
|
+
external_exports.null(),
|
|
11136
|
+
external_exports.string(),
|
|
11137
|
+
external_exports.number(),
|
|
11138
|
+
external_exports.boolean(),
|
|
11139
|
+
external_exports.array(StateTransitionInputBindingSchema),
|
|
11140
|
+
external_exports.object({
|
|
11141
|
+
const: external_exports.unknown()
|
|
11142
|
+
}).strict(),
|
|
11143
|
+
external_exports.object({
|
|
11144
|
+
from: external_exports.literal("object"),
|
|
11145
|
+
path: external_exports.string().min(1),
|
|
11146
|
+
editable: external_exports.boolean().optional()
|
|
11147
|
+
}).strict(),
|
|
11148
|
+
external_exports.object({
|
|
11149
|
+
from: external_exports.literal("field"),
|
|
11150
|
+
name: external_exports.string().min(1),
|
|
11151
|
+
editable: external_exports.boolean().optional()
|
|
11152
|
+
}).strict(),
|
|
11153
|
+
external_exports.object({
|
|
11154
|
+
from: external_exports.literal("relationship"),
|
|
11155
|
+
name: external_exports.string().min(1),
|
|
11156
|
+
path: external_exports.string().min(1).optional(),
|
|
11157
|
+
many: external_exports.boolean().optional(),
|
|
11158
|
+
editable: external_exports.boolean().optional()
|
|
11159
|
+
}).strict(),
|
|
11160
|
+
external_exports.object({
|
|
11161
|
+
from: external_exports.literal("session"),
|
|
11162
|
+
path: external_exports.string().min(1),
|
|
11163
|
+
editable: external_exports.boolean().optional()
|
|
11164
|
+
}).strict(),
|
|
11165
|
+
external_exports.object({
|
|
11166
|
+
from: external_exports.literal("actor"),
|
|
11167
|
+
path: external_exports.string().min(1),
|
|
11168
|
+
editable: external_exports.boolean().optional()
|
|
11169
|
+
}).strict(),
|
|
11170
|
+
external_exports.record(external_exports.string(), StateTransitionInputBindingSchema)
|
|
11171
|
+
])
|
|
11172
|
+
);
|
|
11173
|
+
var StateTransitionActionSchema = external_exports.object({
|
|
11174
|
+
effect: external_exports.string().min(1),
|
|
11175
|
+
input: external_exports.record(external_exports.string(), StateTransitionInputBindingSchema).optional()
|
|
11176
|
+
}).strict();
|
|
11177
|
+
var StateTransitionAssigneeSchema = external_exports.object({
|
|
11178
|
+
kind: external_exports.string().min(1),
|
|
11179
|
+
from: StateTransitionInputBindingSchema.optional(),
|
|
11180
|
+
role: external_exports.string().optional(),
|
|
11181
|
+
label: external_exports.string().optional()
|
|
11182
|
+
}).strict();
|
|
11183
|
+
var StateTransitionRelatedStateRequirementSchema = external_exports.object({
|
|
11184
|
+
relationship: external_exports.string().min(1),
|
|
11185
|
+
machine: external_exports.string().min(1),
|
|
11186
|
+
state: external_exports.string().min(1),
|
|
11187
|
+
className: external_exports.string().min(1).optional(),
|
|
11188
|
+
label: external_exports.string().optional(),
|
|
11189
|
+
mode: external_exports.enum(["every", "some", "any"]).optional()
|
|
11190
|
+
}).strict();
|
|
11191
|
+
var StateTransitionRequirementsSchema = external_exports.object({
|
|
11192
|
+
fields: external_exports.array(external_exports.string().min(1)).optional(),
|
|
11193
|
+
relationships: external_exports.array(external_exports.string().min(1)).optional(),
|
|
11194
|
+
relatedStates: external_exports.array(StateTransitionRelatedStateRequirementSchema).optional()
|
|
11195
|
+
}).strict();
|
|
11196
|
+
var StateTransitionPermissionSchema = external_exports.union([
|
|
11197
|
+
external_exports.string().min(1),
|
|
11198
|
+
external_exports.object({
|
|
11199
|
+
profile: external_exports.string().min(1).optional(),
|
|
11200
|
+
profileId: external_exports.string().min(1).optional(),
|
|
11201
|
+
label: external_exports.string().optional(),
|
|
11202
|
+
reason: external_exports.string().optional()
|
|
11203
|
+
}).strict()
|
|
11204
|
+
]);
|
|
11205
|
+
var StateTransitionExpectedOutcomeSchema = external_exports.union([
|
|
11206
|
+
external_exports.string().min(1),
|
|
11207
|
+
external_exports.object({
|
|
11208
|
+
machine: external_exports.string().min(1).optional(),
|
|
11209
|
+
state: external_exports.string().min(1),
|
|
11210
|
+
summary: external_exports.string().optional()
|
|
11211
|
+
}).strict()
|
|
11212
|
+
]);
|
|
10799
11213
|
var StateMachineTransitionSchema = external_exports.object({
|
|
10800
11214
|
name: external_exports.string().min(1),
|
|
10801
11215
|
from: external_exports.string().min(1),
|
|
10802
|
-
to: external_exports.string().min(1)
|
|
11216
|
+
to: external_exports.string().min(1),
|
|
11217
|
+
label: external_exports.string().optional(),
|
|
11218
|
+
description: external_exports.string().optional(),
|
|
11219
|
+
action: StateTransitionActionSchema.optional(),
|
|
11220
|
+
assignee: StateTransitionAssigneeSchema.optional(),
|
|
11221
|
+
requirements: StateTransitionRequirementsSchema.optional(),
|
|
11222
|
+
permission: StateTransitionPermissionSchema.optional(),
|
|
11223
|
+
risk: external_exports.enum(["low", "medium", "high"]).optional(),
|
|
11224
|
+
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional()
|
|
10803
11225
|
}).strict();
|
|
10804
11226
|
external_exports.object({
|
|
10805
11227
|
name: external_exports.string().min(1),
|
|
11228
|
+
stateField: external_exports.string().min(1).optional(),
|
|
10806
11229
|
entryState: external_exports.string().min(1),
|
|
10807
11230
|
states: external_exports.array(StateMachineStateSchema).min(1),
|
|
10808
11231
|
transitions: external_exports.array(StateMachineTransitionSchema),
|
|
@@ -10869,6 +11292,16 @@ var PoliciesSchema = external_exports.object({
|
|
|
10869
11292
|
confirmWhen: external_exports.array(PolicyRuleSchema).optional(),
|
|
10870
11293
|
denyWhen: external_exports.array(PolicyRuleSchema).optional()
|
|
10871
11294
|
}).strict();
|
|
11295
|
+
var CreatesSchema = external_exports.union([
|
|
11296
|
+
external_exports.string().min(1),
|
|
11297
|
+
external_exports.object({
|
|
11298
|
+
className: external_exports.string().min(1),
|
|
11299
|
+
idPath: external_exports.string().min(1).optional(),
|
|
11300
|
+
pathPath: external_exports.string().min(1).optional(),
|
|
11301
|
+
statePath: external_exports.string().min(1).optional(),
|
|
11302
|
+
classStateHandle: external_exports.boolean().optional()
|
|
11303
|
+
}).strict()
|
|
11304
|
+
]);
|
|
10872
11305
|
external_exports.object({
|
|
10873
11306
|
postCondition: external_exports.union([
|
|
10874
11307
|
external_exports.string(),
|
|
@@ -10899,6 +11332,7 @@ external_exports.object({
|
|
|
10899
11332
|
mode: external_exports.string().optional()
|
|
10900
11333
|
}).strict()
|
|
10901
11334
|
]).optional(),
|
|
11335
|
+
creates: CreatesSchema.optional(),
|
|
10902
11336
|
access: external_exports.enum(["read", "write", "ui"]).optional(),
|
|
10903
11337
|
effectKind: external_exports.enum(["read", "write", "ui"]).optional(),
|
|
10904
11338
|
sideEffect: external_exports.enum(["read", "write", "ui", "readonly", "read_only"]).optional(),
|
|
@@ -11126,9 +11560,10 @@ function mergeMethodSummaryPatch(target, patch) {
|
|
|
11126
11560
|
if (patch.metamodels !== void 0) target.metamodels = patch.metamodels;
|
|
11127
11561
|
if (patch.effectBehaviors !== void 0)
|
|
11128
11562
|
target.effectBehaviors = patch.effectBehaviors;
|
|
11563
|
+
if (patch.creates !== void 0) target.creates = patch.creates;
|
|
11129
11564
|
if (patch.static !== void 0) target.static = patch.static;
|
|
11130
11565
|
}
|
|
11131
|
-
function
|
|
11566
|
+
function toPascalCase2(value) {
|
|
11132
11567
|
return value.split(/[_:\-\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
11133
11568
|
}
|
|
11134
11569
|
function normalizeNotesInput(input) {
|
|
@@ -11186,29 +11621,60 @@ function normalizeEffectBehaviorSummary(metamodels) {
|
|
|
11186
11621
|
}
|
|
11187
11622
|
return Object.keys(result).length > 0 ? result : null;
|
|
11188
11623
|
}
|
|
11189
|
-
function
|
|
11190
|
-
if (!
|
|
11191
|
-
|
|
11624
|
+
function normalizeCreationSummary(metamodels) {
|
|
11625
|
+
if (!isObject(metamodels)) return null;
|
|
11626
|
+
let raw = metamodels.creates;
|
|
11627
|
+
if (typeof raw === "string" && raw.trim().length > 0) {
|
|
11628
|
+
const trimmed = raw.trim();
|
|
11629
|
+
if (trimmed.startsWith("{") || trimmed.startsWith('"')) {
|
|
11630
|
+
try {
|
|
11631
|
+
raw = JSON.parse(trimmed);
|
|
11632
|
+
} catch {
|
|
11633
|
+
return { className: trimmed };
|
|
11634
|
+
}
|
|
11635
|
+
} else {
|
|
11636
|
+
return { className: trimmed };
|
|
11637
|
+
}
|
|
11638
|
+
}
|
|
11639
|
+
if (typeof raw === "string" && raw.trim().length > 0) {
|
|
11640
|
+
return { className: raw.trim() };
|
|
11192
11641
|
}
|
|
11642
|
+
if (!isObject(raw)) return null;
|
|
11643
|
+
const className = typeof raw.className === "string" && raw.className.trim() ? raw.className.trim() : "";
|
|
11644
|
+
if (!className) return null;
|
|
11645
|
+
return {
|
|
11646
|
+
className,
|
|
11647
|
+
...typeof raw.idPath === "string" && raw.idPath.trim() ? { idPath: raw.idPath.trim() } : {},
|
|
11648
|
+
...typeof raw.pathPath === "string" && raw.pathPath.trim() ? { pathPath: raw.pathPath.trim() } : {},
|
|
11649
|
+
...typeof raw.statePath === "string" && raw.statePath.trim() ? { statePath: raw.statePath.trim() } : {},
|
|
11650
|
+
...typeof raw.classStateHandle === "boolean" ? { classStateHandle: raw.classStateHandle } : {}
|
|
11651
|
+
};
|
|
11652
|
+
}
|
|
11653
|
+
function buildEffectBehaviorDocs(effectBehaviors, creates) {
|
|
11193
11654
|
const docs = [];
|
|
11194
|
-
if (
|
|
11655
|
+
if (creates) {
|
|
11656
|
+
docs.push(
|
|
11657
|
+
`Creation method: creates ${creates.className}. The agent may use generated class-level new-record action methods for this class.`
|
|
11658
|
+
);
|
|
11659
|
+
}
|
|
11660
|
+
if (effectBehaviors?.approvalRequired?.required) {
|
|
11195
11661
|
docs.push(
|
|
11196
11662
|
effectBehaviors.approvalRequired.reason ? `Approval required: ${effectBehaviors.approvalRequired.reason}.` : "Approval required before execution."
|
|
11197
11663
|
);
|
|
11198
11664
|
}
|
|
11199
|
-
if (effectBehaviors
|
|
11665
|
+
if (effectBehaviors?.postCondition) {
|
|
11200
11666
|
docs.push(`Post-condition: ${effectBehaviors.postCondition.condition}.`);
|
|
11201
11667
|
if (effectBehaviors.postCondition.description) {
|
|
11202
11668
|
docs.push(effectBehaviors.postCondition.description);
|
|
11203
11669
|
}
|
|
11204
11670
|
}
|
|
11205
|
-
if (effectBehaviors
|
|
11671
|
+
if (effectBehaviors?.dryRun?.enabled) {
|
|
11206
11672
|
docs.push("Supports dry run.");
|
|
11207
11673
|
if (effectBehaviors.dryRun.description) {
|
|
11208
11674
|
docs.push(effectBehaviors.dryRun.description);
|
|
11209
11675
|
}
|
|
11210
11676
|
}
|
|
11211
|
-
if (effectBehaviors
|
|
11677
|
+
if (effectBehaviors?.reverse) {
|
|
11212
11678
|
if (effectBehaviors.reverse.handler) {
|
|
11213
11679
|
docs.push(`Reverse handler: ${effectBehaviors.reverse.handler}.`);
|
|
11214
11680
|
} else {
|
|
@@ -11267,13 +11733,21 @@ function buildEffectBehaviorMutations(toolPath, spec) {
|
|
|
11267
11733
|
query: `mutation { at(path: ${JSON.stringify(toolPath)}) { set_approval_required(${args}) { kind } } }`
|
|
11268
11734
|
});
|
|
11269
11735
|
}
|
|
11736
|
+
if (spec.creates !== void 0) {
|
|
11737
|
+
mutations.push({
|
|
11738
|
+
label: `set creates on ${toolPath}`,
|
|
11739
|
+
query: `mutation { at(path: ${JSON.stringify(toolPath)}) { create_submodel(subpath: "creates", label: "creates") { set_string_value(value: ${JSON.stringify(
|
|
11740
|
+
JSON.stringify(spec.creates)
|
|
11741
|
+
)}) { done } } } }`
|
|
11742
|
+
});
|
|
11743
|
+
}
|
|
11270
11744
|
return mutations;
|
|
11271
11745
|
}
|
|
11272
11746
|
function readMethodEffectBehaviors(rawMethod) {
|
|
11747
|
+
const metamodels = isObject(rawMethod.metamodels) ? rawMethod.metamodels : null;
|
|
11273
11748
|
return {
|
|
11274
|
-
effectBehaviors: normalizeEffectBehaviorSummary(
|
|
11275
|
-
|
|
11276
|
-
)
|
|
11749
|
+
effectBehaviors: normalizeEffectBehaviorSummary(metamodels),
|
|
11750
|
+
creates: normalizeCreationSummary(metamodels)
|
|
11277
11751
|
};
|
|
11278
11752
|
}
|
|
11279
11753
|
var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
@@ -11295,6 +11769,10 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
11295
11769
|
{
|
|
11296
11770
|
key: "approvalRequired",
|
|
11297
11771
|
description: "Boolean or `{ required, reason, mode }`."
|
|
11772
|
+
},
|
|
11773
|
+
{
|
|
11774
|
+
key: "creates",
|
|
11775
|
+
description: 'Marks a static method as an allowed creator for a class. Use `creates: "class_name"` or `{ className, idPath, pathPath, statePath, classStateHandle }`.'
|
|
11298
11776
|
}
|
|
11299
11777
|
]
|
|
11300
11778
|
},
|
|
@@ -11414,7 +11892,10 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
11414
11892
|
...methodIR,
|
|
11415
11893
|
docs: [
|
|
11416
11894
|
...methodIR.docs,
|
|
11417
|
-
...buildEffectBehaviorDocs(
|
|
11895
|
+
...buildEffectBehaviorDocs(
|
|
11896
|
+
methodSummary.effectBehaviors,
|
|
11897
|
+
methodSummary.creates
|
|
11898
|
+
)
|
|
11418
11899
|
]
|
|
11419
11900
|
};
|
|
11420
11901
|
}
|
|
@@ -11655,15 +12136,50 @@ function toRecordSearchResult(className, node) {
|
|
|
11655
12136
|
return [];
|
|
11656
12137
|
}
|
|
11657
12138
|
) : [];
|
|
12139
|
+
const graphPathId = extractRecordIdFromGraphPath(path, className);
|
|
12140
|
+
const realIdField = fields.find(
|
|
12141
|
+
(field) => normalizeGraphPathSegment(field.name) === "real_id" && typeof field.value === "string" && field.value.trim()
|
|
12142
|
+
);
|
|
12143
|
+
const id = typeof realIdField?.value === "string" ? realIdField.value.trim() : graphPathId;
|
|
12144
|
+
const rawLabel = typeof node.label === "string" && node.label.trim() ? node.label : "";
|
|
12145
|
+
if (fields.length === 0 && rawLabel && isPlaceholderRecordLabel(rawLabel, graphPathId, path)) {
|
|
12146
|
+
return null;
|
|
12147
|
+
}
|
|
12148
|
+
const fallbackLabel = displayLabelFromFields(fields);
|
|
12149
|
+
const label = rawLabel && !isPlaceholderRecordLabel(rawLabel, id, path) ? rawLabel : fallbackLabel || rawLabel || id;
|
|
11658
12150
|
return {
|
|
11659
12151
|
path,
|
|
11660
12152
|
className,
|
|
11661
|
-
id
|
|
11662
|
-
label
|
|
12153
|
+
id,
|
|
12154
|
+
label,
|
|
11663
12155
|
description: typeof node.description === "string" && node.description.trim() ? node.description : null,
|
|
11664
12156
|
fields
|
|
11665
12157
|
};
|
|
11666
12158
|
}
|
|
12159
|
+
function isPlaceholderRecordLabel(label, id, path) {
|
|
12160
|
+
const normalizedLabel = normalizeGraphPathSegment(label);
|
|
12161
|
+
return normalizedLabel === normalizeGraphPathSegment(id) || normalizedLabel === normalizeGraphPathSegment(path);
|
|
12162
|
+
}
|
|
12163
|
+
function displayLabelFromFields(fields) {
|
|
12164
|
+
const preferredFieldNames = [
|
|
12165
|
+
"name",
|
|
12166
|
+
"title",
|
|
12167
|
+
"label",
|
|
12168
|
+
"display_name",
|
|
12169
|
+
"file_name",
|
|
12170
|
+
"number",
|
|
12171
|
+
"code"
|
|
12172
|
+
];
|
|
12173
|
+
for (const preferred of preferredFieldNames) {
|
|
12174
|
+
const match = fields.find(
|
|
12175
|
+
(field) => normalizeGraphPathSegment(field.name) === preferred && typeof field.value === "string" && field.value.trim()
|
|
12176
|
+
);
|
|
12177
|
+
if (typeof match?.value === "string") {
|
|
12178
|
+
return match.value.trim();
|
|
12179
|
+
}
|
|
12180
|
+
}
|
|
12181
|
+
return null;
|
|
12182
|
+
}
|
|
11667
12183
|
function normalizeRecordSearchText(value) {
|
|
11668
12184
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
|
|
11669
12185
|
}
|
|
@@ -11805,7 +12321,12 @@ async function recordOpenAIUsageSpend(options) {
|
|
|
11805
12321
|
const metadata = {
|
|
11806
12322
|
...options.metadata || {},
|
|
11807
12323
|
...options.usage.rawUsage !== void 0 ? { openaiUsage: options.usage.rawUsage } : {},
|
|
11808
|
-
usageContext: context
|
|
12324
|
+
usageContext: context,
|
|
12325
|
+
pricingContextTier: options.usage.pricingContextTier,
|
|
12326
|
+
cacheWritePricePerMillionMicros: options.usage.cacheWritePricePerMillionMicros,
|
|
12327
|
+
cacheWriteTokens: options.usage.cacheWriteTokens,
|
|
12328
|
+
cacheWriteCostMicros: options.usage.cacheWriteCostMicros,
|
|
12329
|
+
longContextThresholdTokens: options.usage.longContextThresholdTokens
|
|
11809
12330
|
};
|
|
11810
12331
|
const response = await fetch(
|
|
11811
12332
|
`${toGranularHttpBase(options.apiUrl)}/control/spend/events`,
|
|
@@ -12563,15 +13084,47 @@ var searchableMetamodelPackage = defineMetamodelPackage({
|
|
|
12563
13084
|
|
|
12564
13085
|
// ../metamodel-state-machine/src/index.ts
|
|
12565
13086
|
function normalizeStateMachines(values) {
|
|
13087
|
+
const parseJsonRecord = (value) => {
|
|
13088
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
13089
|
+
return value;
|
|
13090
|
+
}
|
|
13091
|
+
if (typeof value !== "string" || !value.trim()) return null;
|
|
13092
|
+
try {
|
|
13093
|
+
const parsed = JSON.parse(value);
|
|
13094
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
13095
|
+
} catch {
|
|
13096
|
+
return null;
|
|
13097
|
+
}
|
|
13098
|
+
};
|
|
13099
|
+
const parseJsonValue = (value) => {
|
|
13100
|
+
if (value === null || typeof value === "undefined") return null;
|
|
13101
|
+
if (typeof value !== "string") return value;
|
|
13102
|
+
if (!value.trim()) return null;
|
|
13103
|
+
try {
|
|
13104
|
+
return JSON.parse(value);
|
|
13105
|
+
} catch {
|
|
13106
|
+
return value;
|
|
13107
|
+
}
|
|
13108
|
+
};
|
|
12566
13109
|
return (values || []).map((machine) => {
|
|
12567
13110
|
const states = (machine?.states || []).map((state) => ({
|
|
12568
13111
|
name: String(state?.name || ""),
|
|
12569
|
-
|
|
13112
|
+
label: typeof state?.label === "string" ? state.label : null,
|
|
13113
|
+
description: typeof state?.description === "string" ? state.description : null,
|
|
13114
|
+
isFinal: Boolean(state?.is_final ?? state?.isFinal)
|
|
12570
13115
|
})).filter((state) => state.name.length > 0);
|
|
12571
13116
|
const transitions = (machine?.transitions || []).map((transition) => ({
|
|
12572
13117
|
name: String(transition?.name || ""),
|
|
12573
13118
|
from: String(transition?.from?.name || ""),
|
|
12574
|
-
to: String(transition?.to?.name || "")
|
|
13119
|
+
to: String(transition?.to?.name || ""),
|
|
13120
|
+
label: typeof transition?.label === "string" ? transition.label : null,
|
|
13121
|
+
description: typeof transition?.description === "string" ? transition.description : null,
|
|
13122
|
+
action: parseJsonRecord(transition?.action) || parseJsonRecord(transition?.action_json),
|
|
13123
|
+
assignee: parseJsonRecord(transition?.assignee) || parseJsonRecord(transition?.assignee_json),
|
|
13124
|
+
requirements: parseJsonRecord(transition?.requirements) || parseJsonRecord(transition?.requirements_json),
|
|
13125
|
+
permission: parseJsonValue(transition?.permission) ?? parseJsonValue(transition?.permission_json),
|
|
13126
|
+
risk: transition?.risk === "low" || transition?.risk === "medium" || transition?.risk === "high" ? transition.risk : null,
|
|
13127
|
+
expectedOutcome: parseJsonValue(transition?.expectedOutcome) ?? parseJsonValue(transition?.expected_outcome_json)
|
|
12575
13128
|
})).filter(
|
|
12576
13129
|
(transition) => transition.name.length > 0 && transition.from.length > 0 && transition.to.length > 0
|
|
12577
13130
|
);
|
|
@@ -12585,7 +13138,7 @@ function normalizeStateMachines(values) {
|
|
|
12585
13138
|
}).filter((machine) => machine.name.length > 0);
|
|
12586
13139
|
}
|
|
12587
13140
|
function stateTypeName(className, machineName) {
|
|
12588
|
-
return `${
|
|
13141
|
+
return `${toPascalCase2(className)}${toPascalCase2(machineName)}`;
|
|
12589
13142
|
}
|
|
12590
13143
|
function transitionTypeName(className, machineName) {
|
|
12591
13144
|
return `${stateTypeName(className, machineName)}Transition`;
|
|
@@ -12593,6 +13146,15 @@ function transitionTypeName(className, machineName) {
|
|
|
12593
13146
|
function pathTypeName(className, machineName) {
|
|
12594
13147
|
return `${stateTypeName(className, machineName)}Path`;
|
|
12595
13148
|
}
|
|
13149
|
+
function methodToken(value) {
|
|
13150
|
+
const token = String(value || "").trim().replace(/[^A-Za-z0-9_]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
13151
|
+
return token || "state";
|
|
13152
|
+
}
|
|
13153
|
+
function transitionActionsForMachine(machine) {
|
|
13154
|
+
return Object.fromEntries(
|
|
13155
|
+
(machine.transitions || []).filter((transition) => transition.action?.effect).map((transition) => [transition.name, transition.action])
|
|
13156
|
+
);
|
|
13157
|
+
}
|
|
12596
13158
|
function normalizeStateDefinitions(machine) {
|
|
12597
13159
|
const finalStates = new Set(machine.finalStates || []);
|
|
12598
13160
|
const states = /* @__PURE__ */ new Map();
|
|
@@ -12606,6 +13168,8 @@ function normalizeStateDefinitions(machine) {
|
|
|
12606
13168
|
}
|
|
12607
13169
|
states.set(rawState.name, {
|
|
12608
13170
|
name: rawState.name,
|
|
13171
|
+
label: rawState.label,
|
|
13172
|
+
description: rawState.description,
|
|
12609
13173
|
isFinal: Boolean(rawState.isFinal) || finalStates.has(rawState.name)
|
|
12610
13174
|
});
|
|
12611
13175
|
}
|
|
@@ -12617,6 +13181,44 @@ function normalizeStateDefinitions(machine) {
|
|
|
12617
13181
|
}
|
|
12618
13182
|
return [...states.values()];
|
|
12619
13183
|
}
|
|
13184
|
+
function transitionMetadataGraphqlArgs(transition) {
|
|
13185
|
+
const args = [];
|
|
13186
|
+
if (typeof transition.label === "string") {
|
|
13187
|
+
args.push(`label: ${JSON.stringify(transition.label)}`);
|
|
13188
|
+
}
|
|
13189
|
+
if (typeof transition.description === "string") {
|
|
13190
|
+
args.push(`description: ${JSON.stringify(transition.description)}`);
|
|
13191
|
+
}
|
|
13192
|
+
if (transition.action) {
|
|
13193
|
+
args.push(
|
|
13194
|
+
`action_json: ${JSON.stringify(JSON.stringify(transition.action))}`
|
|
13195
|
+
);
|
|
13196
|
+
}
|
|
13197
|
+
if (transition.assignee) {
|
|
13198
|
+
args.push(
|
|
13199
|
+
`assignee_json: ${JSON.stringify(JSON.stringify(transition.assignee))}`
|
|
13200
|
+
);
|
|
13201
|
+
}
|
|
13202
|
+
if (transition.requirements) {
|
|
13203
|
+
args.push(
|
|
13204
|
+
`requirements_json: ${JSON.stringify(JSON.stringify(transition.requirements))}`
|
|
13205
|
+
);
|
|
13206
|
+
}
|
|
13207
|
+
if (transition.permission) {
|
|
13208
|
+
args.push(
|
|
13209
|
+
`permission_json: ${JSON.stringify(JSON.stringify(transition.permission))}`
|
|
13210
|
+
);
|
|
13211
|
+
}
|
|
13212
|
+
if (transition.risk) {
|
|
13213
|
+
args.push(`risk: ${JSON.stringify(transition.risk)}`);
|
|
13214
|
+
}
|
|
13215
|
+
if (transition.expectedOutcome) {
|
|
13216
|
+
args.push(
|
|
13217
|
+
`expected_outcome_json: ${JSON.stringify(JSON.stringify(transition.expectedOutcome))}`
|
|
13218
|
+
);
|
|
13219
|
+
}
|
|
13220
|
+
return args.length > 0 ? `, ${args.join(", ")}` : "";
|
|
13221
|
+
}
|
|
12620
13222
|
function buildStateMachineModelMutations(modelPath, machines) {
|
|
12621
13223
|
const mutations = [];
|
|
12622
13224
|
for (const machine of machines || []) {
|
|
@@ -12627,12 +13229,13 @@ function buildStateMachineModelMutations(modelPath, machines) {
|
|
|
12627
13229
|
)}, entry_state: ${JSON.stringify(machine.entryState)}) { name } } }`
|
|
12628
13230
|
});
|
|
12629
13231
|
for (const state of normalizeStateDefinitions(machine)) {
|
|
12630
|
-
if (state.name === machine.entryState && !state.isFinal)
|
|
13232
|
+
if (state.name === machine.entryState && !state.isFinal && !state.label && !state.description)
|
|
13233
|
+
continue;
|
|
12631
13234
|
mutations.push({
|
|
12632
13235
|
label: `add state ${state.name} on ${modelPath}.${machine.name}`,
|
|
12633
13236
|
query: `mutation { at(path: ${JSON.stringify(modelPath)}) { state_machine(name: ${JSON.stringify(
|
|
12634
13237
|
machine.name
|
|
12635
|
-
)}) { add_state(name: ${JSON.stringify(state.name)}, is_final: ${state.isFinal}) { name } } } }`
|
|
13238
|
+
)}) { add_state(name: ${JSON.stringify(state.name)}, is_final: ${state.isFinal}, label: ${JSON.stringify(state.label || null)}, description: ${JSON.stringify(state.description || null)}) { name } } } }`
|
|
12636
13239
|
});
|
|
12637
13240
|
}
|
|
12638
13241
|
for (const transition of machine.transitions || []) {
|
|
@@ -12644,18 +13247,27 @@ function buildStateMachineModelMutations(modelPath, machines) {
|
|
|
12644
13247
|
transition.name
|
|
12645
13248
|
)}, from: ${JSON.stringify(transition.from)}, to: ${JSON.stringify(
|
|
12646
13249
|
transition.to
|
|
12647
|
-
)}) { name } } } }`
|
|
13250
|
+
)}${transitionMetadataGraphqlArgs(transition)}) { name } } } }`
|
|
12648
13251
|
});
|
|
12649
13252
|
}
|
|
12650
13253
|
}
|
|
12651
13254
|
return mutations;
|
|
12652
13255
|
}
|
|
12653
13256
|
function buildMachineTypes(classSummary, machine) {
|
|
13257
|
+
const stateGlossary = machine.states.map((state) => {
|
|
13258
|
+
const label = state.label && state.label !== state.name ? state.label : null;
|
|
13259
|
+
const meaning = [label, state.description].filter(Boolean).join(" \u2014 ");
|
|
13260
|
+
const finalMarker = state.isFinal ? " Final state." : "";
|
|
13261
|
+
return `${state.name}${meaning ? `: ${meaning}` : "."}${finalMarker}`;
|
|
13262
|
+
});
|
|
12654
13263
|
return [
|
|
12655
13264
|
{
|
|
12656
13265
|
kind: "union",
|
|
12657
13266
|
name: stateTypeName(classSummary.name, machine.name),
|
|
12658
|
-
docs: [
|
|
13267
|
+
docs: [
|
|
13268
|
+
`Allowed states for ${classSummary.name}.${machine.name}.`,
|
|
13269
|
+
...stateGlossary
|
|
13270
|
+
],
|
|
12659
13271
|
members: machine.states.map((state) => state.name)
|
|
12660
13272
|
},
|
|
12661
13273
|
{
|
|
@@ -12671,7 +13283,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
12671
13283
|
const transitionName = transitionTypeName(classSummary.name, machine.name);
|
|
12672
13284
|
pathTypeName(classSummary.name, machine.name);
|
|
12673
13285
|
const docsPrefix = `${classSummary.name}.${machine.name}`;
|
|
12674
|
-
|
|
13286
|
+
const methods = [
|
|
12675
13287
|
{
|
|
12676
13288
|
name: `get_${machine.name}`,
|
|
12677
13289
|
docs: [`Get the current ${docsPrefix} state.`],
|
|
@@ -12694,7 +13306,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
12694
13306
|
],
|
|
12695
13307
|
static: false,
|
|
12696
13308
|
params: [{ name: "target", type: stateName }],
|
|
12697
|
-
returnType: `Promise<${
|
|
13309
|
+
returnType: `Promise<${toPascalCase2(classSummary.name)}>`,
|
|
12698
13310
|
runtime: {
|
|
12699
13311
|
kind: "state_machine",
|
|
12700
13312
|
machineName: machine.name,
|
|
@@ -12767,6 +13379,99 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
12767
13379
|
}
|
|
12768
13380
|
}
|
|
12769
13381
|
];
|
|
13382
|
+
const creationMethods = (classSummary.methods || []).filter(
|
|
13383
|
+
(method) => method.static === true && Boolean(method.creates) && method.creates?.className === classSummary.name && typeof method.effectKey === "string" && method.effectKey.length > 0
|
|
13384
|
+
);
|
|
13385
|
+
for (const state of machine.states) {
|
|
13386
|
+
const stateNameValue = typeof state === "string" ? state : String(state?.name || "");
|
|
13387
|
+
if (!stateNameValue) continue;
|
|
13388
|
+
const token = methodToken(stateNameValue);
|
|
13389
|
+
methods.push(
|
|
13390
|
+
{
|
|
13391
|
+
name: `reach_${machine.name}_to_${token}`,
|
|
13392
|
+
docs: [`Reach ${docsPrefix} state ${stateNameValue}.`],
|
|
13393
|
+
static: false,
|
|
13394
|
+
params: [],
|
|
13395
|
+
returnType: `Promise<${toPascalCase2(classSummary.name)}>`,
|
|
13396
|
+
runtime: {
|
|
13397
|
+
kind: "state_machine",
|
|
13398
|
+
machineName: machine.name,
|
|
13399
|
+
className: classSummary.name,
|
|
13400
|
+
stateTypeName: stateName,
|
|
13401
|
+
transitionTypeName: transitionName,
|
|
13402
|
+
operation: "reach",
|
|
13403
|
+
targetState: stateNameValue,
|
|
13404
|
+
transitionActions: transitionActionsForMachine(machine)
|
|
13405
|
+
}
|
|
13406
|
+
},
|
|
13407
|
+
{
|
|
13408
|
+
name: `prepare_${machine.name}_to_${token}`,
|
|
13409
|
+
docs: [
|
|
13410
|
+
`Prepare a reviewable artifact that can move ${docsPrefix} to ${stateNameValue}.`
|
|
13411
|
+
],
|
|
13412
|
+
static: false,
|
|
13413
|
+
params: [],
|
|
13414
|
+
returnType: "Promise<SessionArtifactRecord>",
|
|
13415
|
+
runtime: {
|
|
13416
|
+
kind: "state_machine",
|
|
13417
|
+
machineName: machine.name,
|
|
13418
|
+
className: classSummary.name,
|
|
13419
|
+
stateTypeName: stateName,
|
|
13420
|
+
transitionTypeName: transitionName,
|
|
13421
|
+
operation: "prepare_reach",
|
|
13422
|
+
targetState: stateNameValue,
|
|
13423
|
+
transitionActions: transitionActionsForMachine(machine)
|
|
13424
|
+
}
|
|
13425
|
+
}
|
|
13426
|
+
);
|
|
13427
|
+
for (const creationMethod of creationMethods) {
|
|
13428
|
+
const creationRuntime = {
|
|
13429
|
+
kind: "state_machine",
|
|
13430
|
+
machineName: machine.name,
|
|
13431
|
+
className: classSummary.name,
|
|
13432
|
+
stateTypeName: stateName,
|
|
13433
|
+
transitionTypeName: transitionName,
|
|
13434
|
+
operation: "prepare_create_reach",
|
|
13435
|
+
targetState: stateNameValue,
|
|
13436
|
+
transitionActions: transitionActionsForMachine(machine),
|
|
13437
|
+
creation: {
|
|
13438
|
+
methodName: creationMethod.name,
|
|
13439
|
+
effectKey: creationMethod.effectKey || creationMethod.name,
|
|
13440
|
+
inputSchema: creationMethod.inputSchema,
|
|
13441
|
+
outputSchema: creationMethod.outputSchema,
|
|
13442
|
+
creates: creationMethod.creates
|
|
13443
|
+
}
|
|
13444
|
+
};
|
|
13445
|
+
const viaName = `prepare_${machine.name}_to_${token}_via_${methodToken(creationMethod.name)}`;
|
|
13446
|
+
methods.push({
|
|
13447
|
+
name: viaName,
|
|
13448
|
+
docs: [
|
|
13449
|
+
`Prepare a reviewable artifact that will create a new ${classSummary.name} through ${creationMethod.name}, then move ${docsPrefix} to ${stateNameValue}.`
|
|
13450
|
+
],
|
|
13451
|
+
static: true,
|
|
13452
|
+
params: [
|
|
13453
|
+
{ name: "input", type: "Record<string, any>", optional: true }
|
|
13454
|
+
],
|
|
13455
|
+
returnType: "Promise<SessionArtifactRecord>",
|
|
13456
|
+
runtime: creationRuntime
|
|
13457
|
+
});
|
|
13458
|
+
if (creationMethods.length === 1) {
|
|
13459
|
+
methods.push({
|
|
13460
|
+
name: `prepare_${machine.name}_to_${token}`,
|
|
13461
|
+
docs: [
|
|
13462
|
+
`Prepare a reviewable artifact that will create a new ${classSummary.name}, then move ${docsPrefix} to ${stateNameValue}.`
|
|
13463
|
+
],
|
|
13464
|
+
static: true,
|
|
13465
|
+
params: [
|
|
13466
|
+
{ name: "input", type: "Record<string, any>", optional: true }
|
|
13467
|
+
],
|
|
13468
|
+
returnType: "Promise<SessionArtifactRecord>",
|
|
13469
|
+
runtime: creationRuntime
|
|
13470
|
+
});
|
|
13471
|
+
}
|
|
13472
|
+
}
|
|
13473
|
+
}
|
|
13474
|
+
return methods;
|
|
12770
13475
|
}
|
|
12771
13476
|
function readStateMachineSummaries(rawClass) {
|
|
12772
13477
|
return {
|
|
@@ -12789,8 +13494,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12789
13494
|
type StateMachineMutation {
|
|
12790
13495
|
name: String!
|
|
12791
13496
|
state_machine: StateMachine!
|
|
12792
|
-
add_state(name: String!, is_final: Boolean): StateMachineMutation!
|
|
12793
|
-
add_transition(name: String!, from: String!, to: String
|
|
13497
|
+
add_state(name: String!, is_final: Boolean, label: String, description: String): StateMachineMutation!
|
|
13498
|
+
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!
|
|
12794
13499
|
activate_transition(name: String!): StateMachineMutation!
|
|
12795
13500
|
}
|
|
12796
13501
|
|
|
@@ -12807,6 +13512,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12807
13512
|
type StateMachineSnapshotMutation {
|
|
12808
13513
|
snapshot: StateMachineSnapshot!
|
|
12809
13514
|
activate_transition(name: String!): StateMachineSnapshotMutation!
|
|
13515
|
+
observe_state(state: String!, force: Boolean, source: String): StateMachineSnapshotMutation!
|
|
12810
13516
|
}
|
|
12811
13517
|
|
|
12812
13518
|
type StateMachine {
|
|
@@ -12826,6 +13532,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12826
13532
|
|
|
12827
13533
|
type StateMachineState {
|
|
12828
13534
|
name: String!
|
|
13535
|
+
label: String
|
|
13536
|
+
description: String
|
|
12829
13537
|
is_final: Boolean!
|
|
12830
13538
|
}
|
|
12831
13539
|
|
|
@@ -12833,6 +13541,14 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12833
13541
|
name: String!
|
|
12834
13542
|
from: StateMachineState!
|
|
12835
13543
|
to: StateMachineState!
|
|
13544
|
+
label: String
|
|
13545
|
+
description: String
|
|
13546
|
+
action_json: String
|
|
13547
|
+
assignee_json: String
|
|
13548
|
+
requirements_json: String
|
|
13549
|
+
permission_json: String
|
|
13550
|
+
risk: String
|
|
13551
|
+
expected_outcome_json: String
|
|
12836
13552
|
}
|
|
12837
13553
|
|
|
12838
13554
|
type StateMachinePath {
|
|
@@ -12885,23 +13601,47 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12885
13601
|
StateMachineMutation: {
|
|
12886
13602
|
name: (value) => value.name,
|
|
12887
13603
|
state_machine: async (value) => await run(value.target.state_machine(value.name)),
|
|
12888
|
-
add_state: async (value, { name, is_final }) => {
|
|
13604
|
+
add_state: async (value, { name, is_final, label, description }) => {
|
|
12889
13605
|
await run(
|
|
12890
13606
|
value.target.add_state_machine_state(
|
|
12891
13607
|
value.name,
|
|
12892
13608
|
name,
|
|
12893
|
-
is_final ?? false
|
|
13609
|
+
is_final ?? false,
|
|
13610
|
+
label,
|
|
13611
|
+
description
|
|
12894
13612
|
)
|
|
12895
13613
|
);
|
|
12896
13614
|
return value;
|
|
12897
13615
|
},
|
|
12898
|
-
add_transition: async (value, {
|
|
13616
|
+
add_transition: async (value, {
|
|
13617
|
+
name,
|
|
13618
|
+
from: from2,
|
|
13619
|
+
to,
|
|
13620
|
+
label,
|
|
13621
|
+
description,
|
|
13622
|
+
action_json,
|
|
13623
|
+
assignee_json,
|
|
13624
|
+
requirements_json,
|
|
13625
|
+
permission_json,
|
|
13626
|
+
risk,
|
|
13627
|
+
expected_outcome_json
|
|
13628
|
+
}) => {
|
|
12899
13629
|
await run(
|
|
12900
13630
|
value.target.add_state_machine_transition(
|
|
12901
13631
|
value.name,
|
|
12902
13632
|
name,
|
|
12903
|
-
|
|
12904
|
-
to
|
|
13633
|
+
from2,
|
|
13634
|
+
to,
|
|
13635
|
+
{
|
|
13636
|
+
label,
|
|
13637
|
+
description,
|
|
13638
|
+
actionJson: action_json,
|
|
13639
|
+
assigneeJson: assignee_json,
|
|
13640
|
+
requirementsJson: requirements_json,
|
|
13641
|
+
permissionJson: permission_json,
|
|
13642
|
+
risk,
|
|
13643
|
+
expectedOutcomeJson: expected_outcome_json
|
|
13644
|
+
}
|
|
12905
13645
|
)
|
|
12906
13646
|
);
|
|
12907
13647
|
return value;
|
|
@@ -12920,16 +13660,37 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12920
13660
|
value.target.activate_state_machine_transition(value.name, name)
|
|
12921
13661
|
);
|
|
12922
13662
|
return value;
|
|
13663
|
+
},
|
|
13664
|
+
observe_state: async (value, { state, force, source }) => {
|
|
13665
|
+
await run(
|
|
13666
|
+
value.target.observe_state_machine_state(
|
|
13667
|
+
value.name,
|
|
13668
|
+
state,
|
|
13669
|
+
force === true,
|
|
13670
|
+
source
|
|
13671
|
+
)
|
|
13672
|
+
);
|
|
13673
|
+
return value;
|
|
12923
13674
|
}
|
|
12924
13675
|
},
|
|
12925
13676
|
StateMachineState: {
|
|
12926
13677
|
name: (value) => value.name,
|
|
13678
|
+
label: (value) => value.label || null,
|
|
13679
|
+
description: (value) => value.description || null,
|
|
12927
13680
|
is_final: (value) => value.is_final
|
|
12928
13681
|
},
|
|
12929
13682
|
StateMachineTransition: {
|
|
12930
13683
|
name: (value) => value.name,
|
|
12931
13684
|
from: (value) => value.from_state || { name: value.from, is_final: false },
|
|
12932
|
-
to: (value) => value.to_state || { name: value.to, is_final: false }
|
|
13685
|
+
to: (value) => value.to_state || { name: value.to, is_final: false },
|
|
13686
|
+
label: (value) => value.label || null,
|
|
13687
|
+
description: (value) => value.description || null,
|
|
13688
|
+
action_json: (value) => value.action_json || null,
|
|
13689
|
+
assignee_json: (value) => value.assignee_json || null,
|
|
13690
|
+
requirements_json: (value) => value.requirements_json || null,
|
|
13691
|
+
permission_json: (value) => value.permission_json || null,
|
|
13692
|
+
risk: (value) => value.risk || null,
|
|
13693
|
+
expected_outcome_json: (value) => value.expected_outcome_json || null
|
|
12933
13694
|
},
|
|
12934
13695
|
StateMachinePath: {
|
|
12935
13696
|
states: (value) => value.states,
|
|
@@ -12996,6 +13757,14 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12996
13757
|
name
|
|
12997
13758
|
from { name }
|
|
12998
13759
|
to { name }
|
|
13760
|
+
label
|
|
13761
|
+
description
|
|
13762
|
+
action_json
|
|
13763
|
+
assignee_json
|
|
13764
|
+
requirements_json
|
|
13765
|
+
permission_json
|
|
13766
|
+
risk
|
|
13767
|
+
expected_outcome_json
|
|
12999
13768
|
}
|
|
13000
13769
|
}`
|
|
13001
13770
|
]
|
|
@@ -13027,6 +13796,104 @@ function describeRule(rule) {
|
|
|
13027
13796
|
return `${rule.operator} ${String(rule.booleanValue)}`;
|
|
13028
13797
|
return rule.operator;
|
|
13029
13798
|
}
|
|
13799
|
+
function valueAsString(value) {
|
|
13800
|
+
if (typeof value === "string") return value;
|
|
13801
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
13802
|
+
return String(value);
|
|
13803
|
+
}
|
|
13804
|
+
return "";
|
|
13805
|
+
}
|
|
13806
|
+
function valueAsNumber(value) {
|
|
13807
|
+
if (typeof value === "number") return value;
|
|
13808
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
13809
|
+
const parsed = Number(value);
|
|
13810
|
+
return Number.isFinite(parsed) ? parsed : NaN;
|
|
13811
|
+
}
|
|
13812
|
+
return NaN;
|
|
13813
|
+
}
|
|
13814
|
+
function ruleStringValue(rule) {
|
|
13815
|
+
return rule.stringValue ?? rule.string_value ?? "";
|
|
13816
|
+
}
|
|
13817
|
+
function ruleNumberValue(rule) {
|
|
13818
|
+
return rule.numberValue ?? rule.number_value;
|
|
13819
|
+
}
|
|
13820
|
+
function ruleBooleanValue(rule) {
|
|
13821
|
+
return rule.booleanValue ?? rule.boolean_value;
|
|
13822
|
+
}
|
|
13823
|
+
function evaluateValidationRule(value, rule) {
|
|
13824
|
+
const operator = typeof rule.operator === "string" ? rule.operator : "";
|
|
13825
|
+
const stringValue2 = ruleStringValue(rule);
|
|
13826
|
+
const numberValue = ruleNumberValue(rule);
|
|
13827
|
+
const booleanValue = ruleBooleanValue(rule);
|
|
13828
|
+
let passed = true;
|
|
13829
|
+
switch (operator) {
|
|
13830
|
+
case "eq":
|
|
13831
|
+
if (numberValue !== void 0) {
|
|
13832
|
+
passed = valueAsNumber(value) === numberValue;
|
|
13833
|
+
} else if (booleanValue !== void 0) {
|
|
13834
|
+
passed = value === booleanValue;
|
|
13835
|
+
} else {
|
|
13836
|
+
passed = valueAsString(value) === stringValue2;
|
|
13837
|
+
}
|
|
13838
|
+
break;
|
|
13839
|
+
case "neq":
|
|
13840
|
+
if (numberValue !== void 0) {
|
|
13841
|
+
passed = valueAsNumber(value) !== numberValue;
|
|
13842
|
+
} else if (booleanValue !== void 0) {
|
|
13843
|
+
passed = value !== booleanValue;
|
|
13844
|
+
} else {
|
|
13845
|
+
passed = valueAsString(value) !== stringValue2;
|
|
13846
|
+
}
|
|
13847
|
+
break;
|
|
13848
|
+
case "gt":
|
|
13849
|
+
passed = valueAsNumber(value) > (numberValue ?? NaN);
|
|
13850
|
+
break;
|
|
13851
|
+
case "gte":
|
|
13852
|
+
passed = valueAsNumber(value) >= (numberValue ?? NaN);
|
|
13853
|
+
break;
|
|
13854
|
+
case "lt":
|
|
13855
|
+
passed = valueAsNumber(value) < (numberValue ?? NaN);
|
|
13856
|
+
break;
|
|
13857
|
+
case "lte":
|
|
13858
|
+
passed = valueAsNumber(value) <= (numberValue ?? NaN);
|
|
13859
|
+
break;
|
|
13860
|
+
case "true":
|
|
13861
|
+
passed = value === true;
|
|
13862
|
+
break;
|
|
13863
|
+
case "false":
|
|
13864
|
+
passed = value === false;
|
|
13865
|
+
break;
|
|
13866
|
+
case "regex":
|
|
13867
|
+
try {
|
|
13868
|
+
passed = new RegExp(stringValue2).test(valueAsString(value));
|
|
13869
|
+
} catch {
|
|
13870
|
+
passed = false;
|
|
13871
|
+
}
|
|
13872
|
+
break;
|
|
13873
|
+
case "contains":
|
|
13874
|
+
passed = valueAsString(value).includes(stringValue2);
|
|
13875
|
+
break;
|
|
13876
|
+
case "not_contains":
|
|
13877
|
+
passed = !valueAsString(value).includes(stringValue2);
|
|
13878
|
+
break;
|
|
13879
|
+
case "starts_with":
|
|
13880
|
+
passed = valueAsString(value).startsWith(stringValue2);
|
|
13881
|
+
break;
|
|
13882
|
+
case "ends_with":
|
|
13883
|
+
passed = valueAsString(value).endsWith(stringValue2);
|
|
13884
|
+
break;
|
|
13885
|
+
default:
|
|
13886
|
+
passed = true;
|
|
13887
|
+
}
|
|
13888
|
+
return {
|
|
13889
|
+
passed,
|
|
13890
|
+
operator,
|
|
13891
|
+
message: rule.message ?? null
|
|
13892
|
+
};
|
|
13893
|
+
}
|
|
13894
|
+
function validationRuleFailureMessage(path, rule) {
|
|
13895
|
+
return rule.message || `${path} failed ${rule.operator} validation`;
|
|
13896
|
+
}
|
|
13030
13897
|
function normalizeRule(rule) {
|
|
13031
13898
|
const operator = typeof rule?.operator === "string" ? rule.operator : "";
|
|
13032
13899
|
if (operator.length === 0) return null;
|
|
@@ -13213,6 +14080,18 @@ function buildEffectMetamodelMutations(toolPath, spec) {
|
|
|
13213
14080
|
}
|
|
13214
14081
|
|
|
13215
14082
|
// src/client.ts
|
|
14083
|
+
var DEFAULT_CONVERSATION_SESSION_LIST_LIMIT = 100;
|
|
14084
|
+
var MAX_CONVERSATION_SESSION_LIST_LIMIT = 500;
|
|
14085
|
+
var MAX_CONVERSATION_SESSION_LIST_OFFSET = 1e5;
|
|
14086
|
+
function boundedSessionListInteger(value, name, fallback, minimum, maximum) {
|
|
14087
|
+
if (value === void 0) return fallback;
|
|
14088
|
+
if (!Number.isInteger(value) || value < minimum || value > maximum) {
|
|
14089
|
+
throw new RangeError(
|
|
14090
|
+
`Session list ${name} must be an integer between ${minimum} and ${maximum}.`
|
|
14091
|
+
);
|
|
14092
|
+
}
|
|
14093
|
+
return value;
|
|
14094
|
+
}
|
|
13216
14095
|
var STANDARD_MODULES_OPERATIONS = [
|
|
13217
14096
|
{
|
|
13218
14097
|
create: "entity",
|
|
@@ -13249,6 +14128,26 @@ var STANDARD_MODULES_OPERATIONS = [
|
|
|
13249
14128
|
var BUILTIN_MODULES = {
|
|
13250
14129
|
standard_modules: STANDARD_MODULES_OPERATIONS
|
|
13251
14130
|
};
|
|
14131
|
+
function stateNameFromMethodName(methodName) {
|
|
14132
|
+
const raw = methodName.startsWith("to") ? methodName.slice(2) : methodName;
|
|
14133
|
+
return raw.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
14134
|
+
}
|
|
14135
|
+
function appendQueryOptions(searchParams, query) {
|
|
14136
|
+
for (const [key, value] of Object.entries(query || {})) {
|
|
14137
|
+
if (value === null || typeof value === "undefined" || value === "") {
|
|
14138
|
+
continue;
|
|
14139
|
+
}
|
|
14140
|
+
if (value instanceof Date) {
|
|
14141
|
+
searchParams.set(key, value.toISOString());
|
|
14142
|
+
continue;
|
|
14143
|
+
}
|
|
14144
|
+
if (Array.isArray(value)) {
|
|
14145
|
+
if (value.length > 0) searchParams.set(key, value.join(","));
|
|
14146
|
+
continue;
|
|
14147
|
+
}
|
|
14148
|
+
searchParams.set(key, String(value));
|
|
14149
|
+
}
|
|
14150
|
+
}
|
|
13252
14151
|
var DEFAULT_DIRECT_RECORD_OBJECTS_REQUEST_BATCH_SIZE = 100;
|
|
13253
14152
|
var MAX_RECORD_OBJECTS_CONCURRENCY = 16;
|
|
13254
14153
|
var DEFAULT_DIRECT_RECORD_OBJECTS_RETRY_COUNT = 3;
|
|
@@ -13279,8 +14178,20 @@ function bodyInitFromSessionFileUpload(body) {
|
|
|
13279
14178
|
return body;
|
|
13280
14179
|
}
|
|
13281
14180
|
var EFFECT_CATALOG_SYNC_TIMEOUT_MS = 12e4;
|
|
14181
|
+
var EFFECT_CATALOG_SYNC_BATCH_SIZE = 20;
|
|
13282
14182
|
var EFFECT_CATALOG_SYNC_RETRY_COUNT = 3;
|
|
13283
14183
|
var EFFECT_CATALOG_SYNC_RETRY_DELAY_MS = 1e3;
|
|
14184
|
+
function chunkItems(items, batchSize) {
|
|
14185
|
+
const chunks = [];
|
|
14186
|
+
for (let offset = 0; offset < items.length; offset += batchSize) {
|
|
14187
|
+
chunks.push(items.slice(offset, offset + batchSize));
|
|
14188
|
+
}
|
|
14189
|
+
return chunks;
|
|
14190
|
+
}
|
|
14191
|
+
function isUnsupportedEffectCatalogMutation(error) {
|
|
14192
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
14193
|
+
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");
|
|
14194
|
+
}
|
|
13284
14195
|
function planRecordObjectsChunks(records, batchSize) {
|
|
13285
14196
|
const total = records.length;
|
|
13286
14197
|
const size = Math.max(1, Math.min(batchSize, total));
|
|
@@ -13292,6 +14203,23 @@ function planRecordObjectsChunks(records, batchSize) {
|
|
|
13292
14203
|
}
|
|
13293
14204
|
return plans;
|
|
13294
14205
|
}
|
|
14206
|
+
function preserveRecordObjectRealId(record) {
|
|
14207
|
+
const realId = record.id.trim();
|
|
14208
|
+
if (!realId) {
|
|
14209
|
+
return record;
|
|
14210
|
+
}
|
|
14211
|
+
const fields = record.fields || {};
|
|
14212
|
+
if (typeof fields.real_id === "string" && fields.real_id.trim()) {
|
|
14213
|
+
return record;
|
|
14214
|
+
}
|
|
14215
|
+
return {
|
|
14216
|
+
...record,
|
|
14217
|
+
fields: {
|
|
14218
|
+
...fields,
|
|
14219
|
+
real_id: realId
|
|
14220
|
+
}
|
|
14221
|
+
};
|
|
14222
|
+
}
|
|
13295
14223
|
function computeEffectKey2(effect) {
|
|
13296
14224
|
const attachedClass = effect.className?.trim();
|
|
13297
14225
|
if (!attachedClass) {
|
|
@@ -13502,7 +14430,7 @@ var Environment = class _Environment {
|
|
|
13502
14430
|
}
|
|
13503
14431
|
get sessions() {
|
|
13504
14432
|
return {
|
|
13505
|
-
list: async (options) => this.listSessions(options
|
|
14433
|
+
list: async (options = {}) => this.listSessions(options),
|
|
13506
14434
|
create: async (options) => this.createSession(options),
|
|
13507
14435
|
connect: async (sessionId, options) => this.connectSession(sessionId, options),
|
|
13508
14436
|
reopen: async (sessionId, options) => this.reopenSession(sessionId, options),
|
|
@@ -13529,11 +14457,105 @@ var Environment = class _Environment {
|
|
|
13529
14457
|
getAwaitingCount: async () => this.getAwaitingRecordCount()
|
|
13530
14458
|
};
|
|
13531
14459
|
}
|
|
14460
|
+
/**
|
|
14461
|
+
* Mirror product-owned workflow state into Granular without making Granular
|
|
14462
|
+
* own the customer application's state machine.
|
|
14463
|
+
*/
|
|
14464
|
+
async recordState(input) {
|
|
14465
|
+
const { machine, state, ...target } = input;
|
|
14466
|
+
if (!machine.trim()) {
|
|
14467
|
+
throw new Error("State update requires a machine name");
|
|
14468
|
+
}
|
|
14469
|
+
if (!state.trim()) {
|
|
14470
|
+
throw new Error("State update requires a state");
|
|
14471
|
+
}
|
|
14472
|
+
return this.recordObject({
|
|
14473
|
+
className: target.className,
|
|
14474
|
+
id: target.id,
|
|
14475
|
+
...target.label ? { label: target.label } : {},
|
|
14476
|
+
...target.fields ? { fields: target.fields } : {},
|
|
14477
|
+
...target.relationships ? { relationships: target.relationships } : {},
|
|
14478
|
+
states: {
|
|
14479
|
+
[machine.trim()]: {
|
|
14480
|
+
state: state.trim(),
|
|
14481
|
+
...target.source ? { source: target.source } : {},
|
|
14482
|
+
...target.cause ? { cause: target.cause } : {},
|
|
14483
|
+
...target.actorId ? { actorId: target.actorId } : {},
|
|
14484
|
+
...target.observedAt !== void 0 ? { observedAt: target.observedAt } : {},
|
|
14485
|
+
...target.force !== void 0 ? { force: target.force } : {},
|
|
14486
|
+
...target.metadata ? { metadata: target.metadata } : {}
|
|
14487
|
+
}
|
|
14488
|
+
}
|
|
14489
|
+
});
|
|
14490
|
+
}
|
|
14491
|
+
/**
|
|
14492
|
+
* Mirror product-owned workflow state into Granular without making Granular
|
|
14493
|
+
* own the customer application's state machine.
|
|
14494
|
+
*
|
|
14495
|
+
* Example:
|
|
14496
|
+
* `await env.recordState({ className: "spend_request", id, machine: "lifecycle", state: "policy_review", source: "customer_backend" })`
|
|
14497
|
+
*/
|
|
14498
|
+
state(target) {
|
|
14499
|
+
const observe = async (machineName, stateName, input = {}) => {
|
|
14500
|
+
const observedState = input.observedState || input.state || stateName;
|
|
14501
|
+
if (!observedState) {
|
|
14502
|
+
throw new Error("State observation requires a target state");
|
|
14503
|
+
}
|
|
14504
|
+
return this.recordState({
|
|
14505
|
+
...target,
|
|
14506
|
+
machine: machineName,
|
|
14507
|
+
state: observedState,
|
|
14508
|
+
...input.source ? { source: input.source } : {},
|
|
14509
|
+
...input.cause ? { cause: input.cause } : {},
|
|
14510
|
+
...input.actorId ? { actorId: input.actorId } : {},
|
|
14511
|
+
...input.observedAt !== void 0 ? { observedAt: input.observedAt } : {},
|
|
14512
|
+
...input.force !== void 0 ? { force: input.force } : {},
|
|
14513
|
+
...input.metadata ? { metadata: input.metadata } : {}
|
|
14514
|
+
});
|
|
14515
|
+
};
|
|
14516
|
+
return new Proxy(
|
|
14517
|
+
{},
|
|
14518
|
+
{
|
|
14519
|
+
get: (_target, machineProperty) => {
|
|
14520
|
+
if (typeof machineProperty !== "string") return void 0;
|
|
14521
|
+
return new Proxy(
|
|
14522
|
+
{},
|
|
14523
|
+
{
|
|
14524
|
+
get: (_machineTarget, stateProperty) => {
|
|
14525
|
+
if (stateProperty === "to") {
|
|
14526
|
+
return (stateName, input) => observe(machineProperty, stateName, input || {});
|
|
14527
|
+
}
|
|
14528
|
+
if (typeof stateProperty !== "string") return void 0;
|
|
14529
|
+
return (input) => observe(
|
|
14530
|
+
machineProperty,
|
|
14531
|
+
stateNameFromMethodName(stateProperty),
|
|
14532
|
+
input || {}
|
|
14533
|
+
);
|
|
14534
|
+
}
|
|
14535
|
+
}
|
|
14536
|
+
);
|
|
14537
|
+
}
|
|
14538
|
+
}
|
|
14539
|
+
);
|
|
14540
|
+
}
|
|
13532
14541
|
get feedback() {
|
|
13533
14542
|
return {
|
|
13534
14543
|
list: async () => this.listFeedback()
|
|
13535
14544
|
};
|
|
13536
14545
|
}
|
|
14546
|
+
get manualActions() {
|
|
14547
|
+
return {
|
|
14548
|
+
record: (input) => this.recordManualAction(input),
|
|
14549
|
+
list: (options = {}) => this.listManualActions(options),
|
|
14550
|
+
suggest: (options = {}) => this.suggestManualActions(options)
|
|
14551
|
+
};
|
|
14552
|
+
}
|
|
14553
|
+
get artifactApprovals() {
|
|
14554
|
+
return {
|
|
14555
|
+
list: (options = {}) => this.listArtifactApprovals(options),
|
|
14556
|
+
decide: (approvalTaskId, input) => this.decideArtifactApproval(approvalTaskId, input)
|
|
14557
|
+
};
|
|
14558
|
+
}
|
|
13537
14559
|
/**
|
|
13538
14560
|
* Sessionless environments do not own a live transport, so disconnecting the
|
|
13539
14561
|
* environment handle itself is a no-op. This keeps the public surface
|
|
@@ -13543,17 +14565,12 @@ var Environment = class _Environment {
|
|
|
13543
14565
|
*/
|
|
13544
14566
|
async disconnect() {
|
|
13545
14567
|
}
|
|
13546
|
-
async listSessions(
|
|
13547
|
-
|
|
13548
|
-
|
|
13549
|
-
|
|
13550
|
-
|
|
13551
|
-
|
|
13552
|
-
return [...active, ...closed].sort(
|
|
13553
|
-
(left, right) => Date.parse(right.lastSeenAt) - Date.parse(left.lastSeenAt)
|
|
13554
|
-
);
|
|
13555
|
-
}
|
|
13556
|
-
return status === "closed" ? this.granular.listClosedSessions({ environmentId: this.environmentId }) : this.granular.listOpenSessions({ environmentId: this.environmentId });
|
|
14568
|
+
async listSessions(optionsOrStatus = {}) {
|
|
14569
|
+
const options = typeof optionsOrStatus === "string" ? { status: optionsOrStatus } : optionsOrStatus;
|
|
14570
|
+
return this.granular.listSessions({
|
|
14571
|
+
...options,
|
|
14572
|
+
environmentId: this.environmentId
|
|
14573
|
+
});
|
|
13557
14574
|
}
|
|
13558
14575
|
async getUserEnvironmentState(options = {}) {
|
|
13559
14576
|
return this.granular.getUserEnvironmentState({
|
|
@@ -13571,6 +14588,7 @@ var Environment = class _Environment {
|
|
|
13571
14588
|
return this.granular.createSession({
|
|
13572
14589
|
environmentId: this.environmentId,
|
|
13573
14590
|
clientId: options?.clientId,
|
|
14591
|
+
sessionScope: options?.sessionScope,
|
|
13574
14592
|
initialHeap: options?.initialHeap
|
|
13575
14593
|
});
|
|
13576
14594
|
}
|
|
@@ -13612,6 +14630,50 @@ var Environment = class _Environment {
|
|
|
13612
14630
|
const response = await this.controlPlaneRequest(`/control/environments/${this.environmentId}/feedback`);
|
|
13613
14631
|
return Array.isArray(response.items) ? response.items : [];
|
|
13614
14632
|
}
|
|
14633
|
+
async recordManualAction(input) {
|
|
14634
|
+
const body = {
|
|
14635
|
+
...input,
|
|
14636
|
+
...input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {}
|
|
14637
|
+
};
|
|
14638
|
+
return this.controlPlaneRequest(
|
|
14639
|
+
`/control/environments/${this.environmentId}/manual-actions`,
|
|
14640
|
+
{
|
|
14641
|
+
method: "POST",
|
|
14642
|
+
body: JSON.stringify(body)
|
|
14643
|
+
}
|
|
14644
|
+
);
|
|
14645
|
+
}
|
|
14646
|
+
async listManualActions(options = {}) {
|
|
14647
|
+
const query = new URLSearchParams();
|
|
14648
|
+
appendQueryOptions(query, options);
|
|
14649
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
14650
|
+
return this.controlPlaneRequest(`/control/environments/${this.environmentId}/manual-actions${suffix}`);
|
|
14651
|
+
}
|
|
14652
|
+
async suggestManualActions(options = {}) {
|
|
14653
|
+
const query = new URLSearchParams();
|
|
14654
|
+
appendQueryOptions(query, options);
|
|
14655
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
14656
|
+
return this.controlPlaneRequest(
|
|
14657
|
+
`/control/environments/${this.environmentId}/manual-actions/suggestions${suffix}`
|
|
14658
|
+
);
|
|
14659
|
+
}
|
|
14660
|
+
async listArtifactApprovals(options = {}) {
|
|
14661
|
+
const query = new URLSearchParams();
|
|
14662
|
+
appendQueryOptions(query, options);
|
|
14663
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
14664
|
+
return this.controlPlaneRequest(
|
|
14665
|
+
`/control/environments/${this.environmentId}/artifact-approvals${suffix}`
|
|
14666
|
+
);
|
|
14667
|
+
}
|
|
14668
|
+
async decideArtifactApproval(approvalTaskId, input) {
|
|
14669
|
+
return this.controlPlaneRequest(
|
|
14670
|
+
`/control/environments/${this.environmentId}/artifact-approvals/${encodeURIComponent(approvalTaskId)}/decide`,
|
|
14671
|
+
{
|
|
14672
|
+
method: "POST",
|
|
14673
|
+
body: JSON.stringify(input)
|
|
14674
|
+
}
|
|
14675
|
+
);
|
|
14676
|
+
}
|
|
13615
14677
|
getRuntimeBaseUrl() {
|
|
13616
14678
|
return deriveRuntimeBaseUrl(this._apiEndpoint);
|
|
13617
14679
|
}
|
|
@@ -14436,10 +15498,11 @@ var Environment = class _Environment {
|
|
|
14436
15498
|
if (!Array.isArray(records) || records.length === 0) {
|
|
14437
15499
|
return [];
|
|
14438
15500
|
}
|
|
15501
|
+
const recordsToWrite = records.map(preserveRecordObjectRealId);
|
|
14439
15502
|
const batchSize = Math.max(
|
|
14440
15503
|
1,
|
|
14441
15504
|
Math.min(
|
|
14442
|
-
|
|
15505
|
+
recordsToWrite.length,
|
|
14443
15506
|
options?.batchSize ?? DEFAULT_DIRECT_RECORD_OBJECTS_REQUEST_BATCH_SIZE
|
|
14444
15507
|
)
|
|
14445
15508
|
);
|
|
@@ -14447,8 +15510,8 @@ var Environment = class _Environment {
|
|
|
14447
15510
|
MAX_RECORD_OBJECTS_CONCURRENCY,
|
|
14448
15511
|
Math.max(1, options?.concurrency ?? 1)
|
|
14449
15512
|
);
|
|
14450
|
-
const plans = planRecordObjectsChunks(
|
|
14451
|
-
const total =
|
|
15513
|
+
const plans = planRecordObjectsChunks(recordsToWrite, batchSize);
|
|
15514
|
+
const total = recordsToWrite.length;
|
|
14452
15515
|
const results = new Array(total);
|
|
14453
15516
|
const onChunk = options?.onChunkComplete;
|
|
14454
15517
|
for (let waveStart = 0; waveStart < plans.length; waveStart += concurrency) {
|
|
@@ -14519,12 +15582,13 @@ var Environment = class _Environment {
|
|
|
14519
15582
|
* synchronous upserts and fine-grained chunk progress via **`onChunkComplete`**.
|
|
14520
15583
|
*/
|
|
14521
15584
|
async enqueueRecordImport(records, options = {}) {
|
|
15585
|
+
const recordsToImport = records.map(preserveRecordObjectRealId);
|
|
14522
15586
|
return this.controlPlaneRequest(
|
|
14523
15587
|
`/control/environments/${this.environmentId}/record-imports`,
|
|
14524
15588
|
{
|
|
14525
15589
|
method: "POST",
|
|
14526
15590
|
body: JSON.stringify({
|
|
14527
|
-
records,
|
|
15591
|
+
records: recordsToImport,
|
|
14528
15592
|
batchSize: options.batchSize,
|
|
14529
15593
|
setupRunId: options.setupRunId,
|
|
14530
15594
|
writeMode: options.writeMode
|
|
@@ -14632,11 +15696,7 @@ var EnvironmentSession = class extends Session {
|
|
|
14632
15696
|
}
|
|
14633
15697
|
buildSessionDataUrl(path, query) {
|
|
14634
15698
|
const searchParams = new URLSearchParams();
|
|
14635
|
-
|
|
14636
|
-
if (value !== null && typeof value !== "undefined" && value !== "") {
|
|
14637
|
-
searchParams.set(key, String(value));
|
|
14638
|
-
}
|
|
14639
|
-
}
|
|
15699
|
+
appendQueryOptions(searchParams, query);
|
|
14640
15700
|
const queryString = searchParams.toString();
|
|
14641
15701
|
return `${this.environment.runtimeBaseUrl}${this.sessionDataRoutePrefix}/${encodeURIComponent(this.sessionId)}${path}${queryString ? `?${queryString}` : ""}`;
|
|
14642
15702
|
}
|
|
@@ -14719,9 +15779,108 @@ var EnvironmentSession = class extends Session {
|
|
|
14719
15779
|
),
|
|
14720
15780
|
get: (jobId) => this.sessionDataRequest(
|
|
14721
15781
|
`/jobs/${encodeURIComponent(jobId)}`
|
|
15782
|
+
),
|
|
15783
|
+
latest: async (options = {}) => {
|
|
15784
|
+
const page = await this.sessionDataRequest("/jobs", {
|
|
15785
|
+
status: options.status || "all",
|
|
15786
|
+
latest: true,
|
|
15787
|
+
limit: 1
|
|
15788
|
+
});
|
|
15789
|
+
return page.items[0] || null;
|
|
15790
|
+
}
|
|
15791
|
+
};
|
|
15792
|
+
}
|
|
15793
|
+
get artifacts() {
|
|
15794
|
+
return {
|
|
15795
|
+
list: (options = {}) => {
|
|
15796
|
+
const queryOptions = { ...options };
|
|
15797
|
+
if (options.target) {
|
|
15798
|
+
queryOptions.targetClassName = options.target.className;
|
|
15799
|
+
queryOptions.targetId = options.target.id;
|
|
15800
|
+
delete queryOptions.target;
|
|
15801
|
+
}
|
|
15802
|
+
return this.sessionDataRequest("/artifacts", queryOptions);
|
|
15803
|
+
},
|
|
15804
|
+
listForLatestJob: (options = {}) => this.artifacts.list({
|
|
15805
|
+
...options,
|
|
15806
|
+
latestJob: true
|
|
15807
|
+
}),
|
|
15808
|
+
get: (artifactId) => this.sessionDataRequest(
|
|
15809
|
+
`/artifacts/${encodeURIComponent(artifactId)}`
|
|
15810
|
+
),
|
|
15811
|
+
create: (artifact) => this.sessionDataRequest(
|
|
15812
|
+
"/artifacts",
|
|
15813
|
+
void 0,
|
|
15814
|
+
{
|
|
15815
|
+
method: "POST",
|
|
15816
|
+
body: artifact
|
|
15817
|
+
}
|
|
15818
|
+
),
|
|
15819
|
+
updateInputs: (artifactId, patch) => this.sessionDataRequest(
|
|
15820
|
+
`/artifacts/${encodeURIComponent(artifactId)}`,
|
|
15821
|
+
void 0,
|
|
15822
|
+
{
|
|
15823
|
+
method: "PATCH",
|
|
15824
|
+
body: patch
|
|
15825
|
+
}
|
|
15826
|
+
),
|
|
15827
|
+
validate: (artifactId) => this.sessionDataRequest(
|
|
15828
|
+
`/artifacts/${encodeURIComponent(artifactId)}/validate`,
|
|
15829
|
+
void 0,
|
|
15830
|
+
{ method: "POST" }
|
|
15831
|
+
),
|
|
15832
|
+
execute: (artifactId, options) => this.sessionDataRequest(
|
|
15833
|
+
`/artifacts/${encodeURIComponent(artifactId)}/execute`,
|
|
15834
|
+
void 0,
|
|
15835
|
+
{ method: "POST", body: options }
|
|
15836
|
+
),
|
|
15837
|
+
approve: (artifactId, options) => this.sessionDataRequest(
|
|
15838
|
+
`/artifacts/${encodeURIComponent(artifactId)}/approve`,
|
|
15839
|
+
void 0,
|
|
15840
|
+
{ method: "POST", body: options }
|
|
15841
|
+
),
|
|
15842
|
+
cancel: (artifactId) => this.sessionDataRequest(
|
|
15843
|
+
`/artifacts/${encodeURIComponent(artifactId)}/cancel`,
|
|
15844
|
+
void 0,
|
|
15845
|
+
{ method: "POST" }
|
|
14722
15846
|
)
|
|
14723
15847
|
};
|
|
14724
15848
|
}
|
|
15849
|
+
get manualActions() {
|
|
15850
|
+
const useDelegatedBrowserRoute = this.sessionDataRoutePrefix === "/sdk/browser-sessions";
|
|
15851
|
+
return {
|
|
15852
|
+
record: (input) => useDelegatedBrowserRoute ? this.sessionDataRequest(
|
|
15853
|
+
"/manual-actions",
|
|
15854
|
+
void 0,
|
|
15855
|
+
{
|
|
15856
|
+
method: "POST",
|
|
15857
|
+
body: { ...input, sessionId: this.sessionId }
|
|
15858
|
+
}
|
|
15859
|
+
) : this.environment.manualActions.record({
|
|
15860
|
+
...input,
|
|
15861
|
+
sessionId: this.sessionId
|
|
15862
|
+
}),
|
|
15863
|
+
list: (options = {}) => useDelegatedBrowserRoute ? this.sessionDataRequest("/manual-actions", { ...options, sessionId: this.sessionId }) : this.environment.manualActions.list({
|
|
15864
|
+
...options,
|
|
15865
|
+
sessionId: this.sessionId
|
|
15866
|
+
}),
|
|
15867
|
+
suggest: (options = {}) => useDelegatedBrowserRoute ? this.sessionDataRequest(
|
|
15868
|
+
"/manual-actions/suggestions",
|
|
15869
|
+
options
|
|
15870
|
+
) : this.environment.manualActions.suggest(options)
|
|
15871
|
+
};
|
|
15872
|
+
}
|
|
15873
|
+
get artifactApprovals() {
|
|
15874
|
+
const useDelegatedBrowserRoute = this.sessionDataRoutePrefix === "/sdk/browser-sessions";
|
|
15875
|
+
return {
|
|
15876
|
+
list: (options = {}) => useDelegatedBrowserRoute ? this.sessionDataRequest("/artifact-approvals", options) : this.environment.artifactApprovals.list(options),
|
|
15877
|
+
decide: (approvalTaskId, input) => useDelegatedBrowserRoute ? this.sessionDataRequest(
|
|
15878
|
+
`/artifact-approvals/${encodeURIComponent(approvalTaskId)}/decide`,
|
|
15879
|
+
void 0,
|
|
15880
|
+
{ method: "POST", body: input }
|
|
15881
|
+
) : this.environment.artifactApprovals.decide(approvalTaskId, input)
|
|
15882
|
+
};
|
|
15883
|
+
}
|
|
14725
15884
|
get files() {
|
|
14726
15885
|
return {
|
|
14727
15886
|
list: (options = {}) => this.sessionDataRequest(
|
|
@@ -14802,13 +15961,16 @@ var EnvironmentSession = class extends Session {
|
|
|
14802
15961
|
get transcript() {
|
|
14803
15962
|
return {
|
|
14804
15963
|
list: async (options = {}) => {
|
|
14805
|
-
const [messages, jobs, entries, lists] = await Promise.all([
|
|
15964
|
+
const [messages, jobs, entries, lists, artifacts] = await Promise.all([
|
|
14806
15965
|
this.collectAllSessionItems(this.messages.list),
|
|
14807
15966
|
this.collectAllSessionItems(
|
|
14808
15967
|
(pageOptions) => this.jobs.list({ ...pageOptions, status: "all" })
|
|
14809
15968
|
),
|
|
14810
15969
|
this.collectAllSessionItems(this.heap.entries.list),
|
|
14811
|
-
this.collectAllSessionItems(this.heap.lists.list)
|
|
15970
|
+
this.collectAllSessionItems(this.heap.lists.list),
|
|
15971
|
+
this.collectAllSessionItems(
|
|
15972
|
+
(pageOptions) => this.artifacts.list({ ...pageOptions, status: "all" })
|
|
15973
|
+
)
|
|
14812
15974
|
]);
|
|
14813
15975
|
const liveDoc = {
|
|
14814
15976
|
conversation: { messages },
|
|
@@ -14822,6 +15984,21 @@ var EnvironmentSession = class extends Session {
|
|
|
14822
15984
|
(entry) => Boolean(entry)
|
|
14823
15985
|
)
|
|
14824
15986
|
)
|
|
15987
|
+
},
|
|
15988
|
+
artifacts: {
|
|
15989
|
+
byId: Object.fromEntries(
|
|
15990
|
+
artifacts.map((artifact) => {
|
|
15991
|
+
return artifact?.artifactId ? [
|
|
15992
|
+
artifact.artifactId,
|
|
15993
|
+
artifact
|
|
15994
|
+
] : null;
|
|
15995
|
+
}).filter(
|
|
15996
|
+
(entry) => Boolean(entry)
|
|
15997
|
+
)
|
|
15998
|
+
),
|
|
15999
|
+
order: artifacts.map((artifact) => artifact?.artifactId).filter(
|
|
16000
|
+
(artifactId) => Boolean(artifactId)
|
|
16001
|
+
)
|
|
14825
16002
|
}
|
|
14826
16003
|
};
|
|
14827
16004
|
const heap = normalizeHeapSnapshot({
|
|
@@ -14898,6 +16075,12 @@ var EnvironmentSession = class extends Session {
|
|
|
14898
16075
|
async recordObject(options) {
|
|
14899
16076
|
return this.environment.recordObject(options);
|
|
14900
16077
|
}
|
|
16078
|
+
async recordState(input) {
|
|
16079
|
+
return this.environment.recordState(input);
|
|
16080
|
+
}
|
|
16081
|
+
state(target) {
|
|
16082
|
+
return this.environment.state(target);
|
|
16083
|
+
}
|
|
14901
16084
|
async recordObjects(records, options) {
|
|
14902
16085
|
return this.environment.recordObjects(records, options);
|
|
14903
16086
|
}
|
|
@@ -14968,7 +16151,7 @@ var EnvironmentSession = class extends Session {
|
|
|
14968
16151
|
* Close only the socket transport without sending `client.goodbye`.
|
|
14969
16152
|
*/
|
|
14970
16153
|
disconnectTransport() {
|
|
14971
|
-
this.client.disconnect();
|
|
16154
|
+
this.client.disconnect({ reason: "Transport detach" });
|
|
14972
16155
|
}
|
|
14973
16156
|
/**
|
|
14974
16157
|
* Backwards-compatible alias for `disconnect()`.
|
|
@@ -15363,16 +16546,71 @@ var Granular = class _Granular {
|
|
|
15363
16546
|
};
|
|
15364
16547
|
}
|
|
15365
16548
|
/**
|
|
15366
|
-
* List
|
|
16549
|
+
* List indexed sessions using ownership filters and bounded pagination.
|
|
16550
|
+
*/
|
|
16551
|
+
async listSessions(options) {
|
|
16552
|
+
const environmentId = options.environmentId?.trim();
|
|
16553
|
+
const sandboxId = options.sandboxId?.trim();
|
|
16554
|
+
const subjectId = options.subjectId?.trim();
|
|
16555
|
+
if (!environmentId && !sandboxId && !subjectId) {
|
|
16556
|
+
throw new Error(
|
|
16557
|
+
"listSessions() requires environmentId, sandboxId, or subjectId so history cannot be scanned accidentally."
|
|
16558
|
+
);
|
|
16559
|
+
}
|
|
16560
|
+
const status = options.status || "active";
|
|
16561
|
+
const allowedStatuses = /* @__PURE__ */ new Set([
|
|
16562
|
+
"active",
|
|
16563
|
+
"closed",
|
|
16564
|
+
"expired",
|
|
16565
|
+
"failed",
|
|
16566
|
+
"timeout",
|
|
16567
|
+
"all"
|
|
16568
|
+
]);
|
|
16569
|
+
if (!allowedStatuses.has(status)) {
|
|
16570
|
+
throw new Error(`Unsupported session status: ${String(status)}`);
|
|
16571
|
+
}
|
|
16572
|
+
const limit = boundedSessionListInteger(
|
|
16573
|
+
options.limit,
|
|
16574
|
+
"limit",
|
|
16575
|
+
DEFAULT_CONVERSATION_SESSION_LIST_LIMIT,
|
|
16576
|
+
1,
|
|
16577
|
+
MAX_CONVERSATION_SESSION_LIST_LIMIT
|
|
16578
|
+
);
|
|
16579
|
+
const offset = boundedSessionListInteger(
|
|
16580
|
+
options.offset,
|
|
16581
|
+
"offset",
|
|
16582
|
+
0,
|
|
16583
|
+
0,
|
|
16584
|
+
MAX_CONVERSATION_SESSION_LIST_OFFSET
|
|
16585
|
+
);
|
|
16586
|
+
const query = new URLSearchParams({
|
|
16587
|
+
limit: String(limit),
|
|
16588
|
+
offset: String(offset)
|
|
16589
|
+
});
|
|
16590
|
+
if (environmentId) query.set("environmentId", environmentId);
|
|
16591
|
+
if (sandboxId) query.set("sandboxId", sandboxId);
|
|
16592
|
+
if (subjectId) query.set("userId", subjectId);
|
|
16593
|
+
if (options.sessionScope?.trim()) {
|
|
16594
|
+
query.set("sessionScope", options.sessionScope.trim());
|
|
16595
|
+
}
|
|
16596
|
+
if (status !== "all") query.set("status", status);
|
|
16597
|
+
const res = await this.request(
|
|
16598
|
+
`/control/sessions?${query.toString()}`
|
|
16599
|
+
);
|
|
16600
|
+
const items = Array.isArray(res.items) ? res.items : [];
|
|
16601
|
+
return items.map((row) => this.normalizeConversationSession(row));
|
|
16602
|
+
}
|
|
16603
|
+
/**
|
|
16604
|
+
* List active (open) sessions for an environment.
|
|
15367
16605
|
*/
|
|
15368
16606
|
async listOpenSessions(filters) {
|
|
15369
|
-
return this.
|
|
16607
|
+
return this.listSessions({ ...filters, status: "active" });
|
|
15370
16608
|
}
|
|
15371
16609
|
/**
|
|
15372
16610
|
* List closed sessions for an environment (conversations that have disconnected).
|
|
15373
16611
|
*/
|
|
15374
16612
|
async listClosedSessions(filters) {
|
|
15375
|
-
return this.
|
|
16613
|
+
return this.listSessions({ ...filters, status: "closed" });
|
|
15376
16614
|
}
|
|
15377
16615
|
async getUserEnvironmentState(options) {
|
|
15378
16616
|
const query = new URLSearchParams({
|
|
@@ -15407,14 +16645,6 @@ var Granular = class _Granular {
|
|
|
15407
16645
|
});
|
|
15408
16646
|
return result.readAtBySessionId || {};
|
|
15409
16647
|
}
|
|
15410
|
-
async listSessionsForEnvironment(environmentId, status) {
|
|
15411
|
-
const query = new URLSearchParams({ environmentId, status });
|
|
15412
|
-
const res = await this.request(
|
|
15413
|
-
`/control/sessions?${query.toString()}`
|
|
15414
|
-
);
|
|
15415
|
-
const items = Array.isArray(res.items) ? res.items : [];
|
|
15416
|
-
return items.map((row) => this.normalizeConversationSession(row));
|
|
15417
|
-
}
|
|
15418
16648
|
normalizeConversationSession(row) {
|
|
15419
16649
|
const sessionId = String(row.sessionId ?? row.session_id ?? "");
|
|
15420
16650
|
const environmentId = String(row.environmentId ?? row.environment_id ?? "");
|
|
@@ -15473,6 +16703,7 @@ var Granular = class _Granular {
|
|
|
15473
16703
|
*/
|
|
15474
16704
|
async createSession(options) {
|
|
15475
16705
|
const clientId = options.clientId || `client_${Date.now()}`;
|
|
16706
|
+
const sessionScope = options.sessionScope?.trim() || void 0;
|
|
15476
16707
|
await this.activateEnvironment(options.environmentId);
|
|
15477
16708
|
const envData = await this.environments.get(options.environmentId);
|
|
15478
16709
|
const environment = this.bindEnvironmentHandle(envData);
|
|
@@ -15481,6 +16712,8 @@ var Granular = class _Granular {
|
|
|
15481
16712
|
body: JSON.stringify({
|
|
15482
16713
|
environmentId: options.environmentId,
|
|
15483
16714
|
clientId,
|
|
16715
|
+
sessionScope,
|
|
16716
|
+
capabilities: sessionScope ? { sessionScope } : void 0,
|
|
15484
16717
|
initialHeap: options.initialHeap
|
|
15485
16718
|
})
|
|
15486
16719
|
});
|
|
@@ -15725,15 +16958,43 @@ var Granular = class _Granular {
|
|
|
15725
16958
|
const effects = Array.from(
|
|
15726
16959
|
this.getSandboxEffectMap(host.sandboxId).values()
|
|
15727
16960
|
).map((effect) => this.serializeEffect(effect));
|
|
15728
|
-
|
|
15729
|
-
|
|
15730
|
-
|
|
15731
|
-
|
|
15732
|
-
|
|
15733
|
-
|
|
15734
|
-
|
|
15735
|
-
|
|
15736
|
-
|
|
16961
|
+
let acceptedCount = 0;
|
|
16962
|
+
const rejected = [];
|
|
16963
|
+
try {
|
|
16964
|
+
await withTimeout(
|
|
16965
|
+
host.wsClient.call("effects.resetCatalog", {}),
|
|
16966
|
+
EFFECT_CATALOG_SYNC_TIMEOUT_MS,
|
|
16967
|
+
`effects.resetCatalog for sandbox ${host.sandboxId}`
|
|
16968
|
+
);
|
|
16969
|
+
for (const batch of chunkItems(effects, EFFECT_CATALOG_SYNC_BATCH_SIZE)) {
|
|
16970
|
+
const result = await withTimeout(
|
|
16971
|
+
host.wsClient.call("effects.addCatalog", {
|
|
16972
|
+
effects: batch
|
|
16973
|
+
}),
|
|
16974
|
+
EFFECT_CATALOG_SYNC_TIMEOUT_MS,
|
|
16975
|
+
`effects.addCatalog for sandbox ${host.sandboxId}`
|
|
16976
|
+
);
|
|
16977
|
+
acceptedCount += typeof result?.acceptedCount === "number" ? result.acceptedCount : 0;
|
|
16978
|
+
if (Array.isArray(result?.rejected)) {
|
|
16979
|
+
rejected.push(...result.rejected);
|
|
16980
|
+
}
|
|
16981
|
+
}
|
|
16982
|
+
} catch (error) {
|
|
16983
|
+
if (!isUnsupportedEffectCatalogMutation(error)) {
|
|
16984
|
+
throw error;
|
|
16985
|
+
}
|
|
16986
|
+
const result = await withTimeout(
|
|
16987
|
+
host.wsClient.call("effects.publishCatalog", {
|
|
16988
|
+
effects
|
|
16989
|
+
}),
|
|
16990
|
+
EFFECT_CATALOG_SYNC_TIMEOUT_MS,
|
|
16991
|
+
`effects.publishCatalog for sandbox ${host.sandboxId}`
|
|
16992
|
+
);
|
|
16993
|
+
acceptedCount = typeof result?.acceptedCount === "number" ? result.acceptedCount : 0;
|
|
16994
|
+
if (Array.isArray(result?.rejected)) {
|
|
16995
|
+
rejected.push(...result.rejected);
|
|
16996
|
+
}
|
|
16997
|
+
}
|
|
15737
16998
|
if (acceptedCount === 0 && rejected.length > 0) {
|
|
15738
16999
|
const detail = rejected.map(
|
|
15739
17000
|
(entry) => `${entry.name || "unknown"}: ${entry.reason || "rejected"}`
|
|
@@ -16965,6 +18226,7 @@ var HARNESS_V3_FRONTEND_ACTIONS_MODULE = "@granular/actions/frontend";
|
|
|
16965
18226
|
var HARNESS_V3_CSV_MODULE = "@granular/utils/csv";
|
|
16966
18227
|
var HARNESS_V3_XLSX_MODULE = "@granular/utils/xlsx";
|
|
16967
18228
|
var LEGACY_SANDBOX_TOOLS_MODULE_PATTERN = "\\.\\/sandbox-tools(?:\\.js)?";
|
|
18229
|
+
var HARNESS_V3_RUNTIME_MODULE_PATTERN = "@granular/(?:agent|session|domain(?:/[A-Za-z_$][\\w$]*)?|actions/(?:backend|frontend)|utils/(?:csv|xlsx))";
|
|
16968
18230
|
function hasNamedModuleImport(source, moduleName, name) {
|
|
16969
18231
|
const escapedModule = moduleName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
16970
18232
|
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -17023,6 +18285,15 @@ function reviewGeneratedJobCode(code, _options = {}) {
|
|
|
17023
18285
|
message: "Generated code must use static top-level ESM imports from the Harness v3 runtime modules. Do not use dynamic import(...)."
|
|
17024
18286
|
});
|
|
17025
18287
|
}
|
|
18288
|
+
if (new RegExp(
|
|
18289
|
+
`import\\s+\\*\\s+as\\s+[A-Za-z_$][\\w$]*\\s+from\\s*['"]${HARNESS_V3_RUNTIME_MODULE_PATTERN}['"]`
|
|
18290
|
+
).test(normalized)) {
|
|
18291
|
+
issues.push({
|
|
18292
|
+
code: "runtime_namespace_import",
|
|
18293
|
+
severity: "error",
|
|
18294
|
+
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"`.'
|
|
18295
|
+
});
|
|
18296
|
+
}
|
|
17026
18297
|
if (/\bprocess\.exit\s*\(/.test(normalized)) {
|
|
17027
18298
|
issues.push({
|
|
17028
18299
|
code: "process_exit",
|
|
@@ -18047,6 +19318,31 @@ function buildGranularAgentHeapBlock(heapSummary) {
|
|
|
18047
19318
|
entries: {}
|
|
18048
19319
|
});
|
|
18049
19320
|
}
|
|
19321
|
+
function buildGranularAgentManualActionMemorySummary(input) {
|
|
19322
|
+
const maxItems = Math.max(1, Math.min(12, input.maxItems ?? 8));
|
|
19323
|
+
const suggestions = (input.suggestions || []).filter((suggestion) => suggestion?.actionKey).slice(0, maxItems).map((suggestion) => ({
|
|
19324
|
+
actionKey: suggestion.actionKey,
|
|
19325
|
+
label: suggestion.label || null,
|
|
19326
|
+
targetClassName: suggestion.targetClassName || null,
|
|
19327
|
+
count: typeof suggestion.count === "number" && Number.isFinite(suggestion.count) ? suggestion.count : null,
|
|
19328
|
+
subjectCount: typeof suggestion.subjectCount === "number" && Number.isFinite(suggestion.subjectCount) ? suggestion.subjectCount : null,
|
|
19329
|
+
successCount: typeof suggestion.successCount === "number" && Number.isFinite(suggestion.successCount) ? suggestion.successCount : null,
|
|
19330
|
+
failureCount: typeof suggestion.failureCount === "number" && Number.isFinite(suggestion.failureCount) ? suggestion.failureCount : null,
|
|
19331
|
+
lastOccurredAt: typeof suggestion.lastOccurredAt === "number" && Number.isFinite(suggestion.lastOccurredAt) ? suggestion.lastOccurredAt : null,
|
|
19332
|
+
sampleTargetIds: Array.isArray(suggestion.sampleTargetIds) ? suggestion.sampleTargetIds.filter(
|
|
19333
|
+
(id) => typeof id === "string" && id.trim().length > 0
|
|
19334
|
+
).slice(0, 6) : []
|
|
19335
|
+
}));
|
|
19336
|
+
return [
|
|
19337
|
+
renderConstBlock("manualActionMemory", {
|
|
19338
|
+
suggestions
|
|
19339
|
+
}),
|
|
19340
|
+
"Use manualActionMemory only as behavioral context for likely next actions. Ground the current target and validate permissions before creating or running prepared actions."
|
|
19341
|
+
].join("\n");
|
|
19342
|
+
}
|
|
19343
|
+
function buildGranularAgentManualActionBlock(manualActionSummary) {
|
|
19344
|
+
return manualActionSummary?.trim() || buildGranularAgentManualActionMemorySummary({ suggestions: [] });
|
|
19345
|
+
}
|
|
18050
19346
|
function projectSessionFileSummary(liveDoc) {
|
|
18051
19347
|
const files = asRecord4(liveDoc?.files);
|
|
18052
19348
|
const byId = asRecord4(files?.byId) || {};
|
|
@@ -18078,8 +19374,12 @@ function buildGranularAgentFileBlock(fileSummary) {
|
|
|
18078
19374
|
function extractRuntimeContractExports(domainBlock) {
|
|
18079
19375
|
const classes = /* @__PURE__ */ new Set();
|
|
18080
19376
|
const actions = /* @__PURE__ */ new Set();
|
|
18081
|
-
const
|
|
18082
|
-
for (const match of domainBlock.matchAll(
|
|
19377
|
+
const classConstPattern = /export\s+declare\s+const\s+([A-Za-z_$][\w$]*)\s*:\s*EntityClass\b/g;
|
|
19378
|
+
for (const match of domainBlock.matchAll(classConstPattern)) {
|
|
19379
|
+
classes.add(match[1]);
|
|
19380
|
+
}
|
|
19381
|
+
const classDeclPattern = /export\s+declare\s+class\s+([A-Za-z_$][\w$]*)\b/g;
|
|
19382
|
+
for (const match of domainBlock.matchAll(classDeclPattern)) {
|
|
18083
19383
|
classes.add(match[1]);
|
|
18084
19384
|
}
|
|
18085
19385
|
const actionPattern = /export\s+declare\s+function\s+([A-Za-z_$][\w$]*)/g;
|
|
@@ -18584,6 +19884,9 @@ function buildGranularAgentSystemPrompt(input) {
|
|
|
18584
19884
|
});
|
|
18585
19885
|
const referentBlock = buildGranularAgentReferentBlock(input.referentSummary);
|
|
18586
19886
|
const loopBlock = buildGranularAgentLoopBlock(input.loopSummary);
|
|
19887
|
+
const manualActionBlock = buildGranularAgentManualActionBlock(
|
|
19888
|
+
input.manualActionSummary
|
|
19889
|
+
);
|
|
18587
19890
|
const knownFactsBlock = renderConstBlock(
|
|
18588
19891
|
"knownFacts",
|
|
18589
19892
|
buildKnownFactsFromCheckpoint(input.checkpoint)
|
|
@@ -18597,16 +19900,16 @@ function buildGranularAgentSystemPrompt(input) {
|
|
|
18597
19900
|
- \`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.
|
|
18598
19901
|
- 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.
|
|
18599
19902
|
- 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.
|
|
18600
|
-
- When \`replyToUser(...)\` mentions a grounded record that should remain clickable/referable, wrap only the visible record label in a self-closing inline reference tag
|
|
18601
|
-
- Treat \`showObjects(...)\` as the UI display call for user-visible records,
|
|
18602
|
-
-
|
|
18603
|
-
-
|
|
18604
|
-
-
|
|
18605
|
-
-
|
|
19903
|
+
- 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.
|
|
19904
|
+
- 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"] })\`.
|
|
19905
|
+
- \`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(...)\`.
|
|
19906
|
+
- 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.
|
|
19907
|
+
- 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()\`.
|
|
19908
|
+
- 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.
|
|
19909
|
+
- 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.
|
|
18606
19910
|
- 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.
|
|
18607
19911
|
- 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.
|
|
18608
|
-
-
|
|
18609
|
-
- 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.
|
|
19912
|
+
- 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.
|
|
18610
19913
|
- 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\`.
|
|
18611
19914
|
- \`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.
|
|
18612
19915
|
- 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.
|
|
@@ -18617,7 +19920,7 @@ function buildGranularAgentSystemPrompt(input) {
|
|
|
18617
19920
|
- When using code, assistant text must be empty or one brief summary.
|
|
18618
19921
|
- Code must be plain runnable JavaScript with top-level await.
|
|
18619
19922
|
- Use [Runtime Imports] as the authoritative module map. Import only listed module exports.
|
|
18620
|
-
- 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.
|
|
19923
|
+
- 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.
|
|
18621
19924
|
- 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.
|
|
18622
19925
|
- 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.
|
|
18623
19926
|
- 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\`.
|
|
@@ -18680,6 +19983,7 @@ ${workflowRules}
|
|
|
18680
19983
|
High-priority execution rules:
|
|
18681
19984
|
- 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.
|
|
18682
19985
|
- 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.
|
|
19986
|
+
- 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.
|
|
18683
19987
|
- 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.
|
|
18684
19988
|
- 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.
|
|
18685
19989
|
- 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.
|
|
@@ -18723,7 +20027,7 @@ Intent resolution:
|
|
|
18723
20027
|
- 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.
|
|
18724
20028
|
- 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.
|
|
18725
20029
|
- 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.
|
|
18726
|
-
- 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
|
|
20030
|
+
- 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.
|
|
18727
20031
|
- 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.
|
|
18728
20032
|
- Never call \`.get({ path: "" })\`; an empty path is not a saved reference.
|
|
18729
20033
|
- 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.
|
|
@@ -18850,20 +20154,10 @@ Ask the user when:
|
|
|
18850
20154
|
- the target is unique but the requested action is unclear
|
|
18851
20155
|
|
|
18852
20156
|
Relationship filters:
|
|
18853
|
-
-
|
|
18854
|
-
-
|
|
18855
|
-
-
|
|
18856
|
-
-
|
|
18857
|
-
- 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.
|
|
18858
|
-
- 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.
|
|
18859
|
-
- Use \`some\` only when the generated TypeScript type says \`ManyRelationFilter\`.
|
|
18860
|
-
- 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.
|
|
18861
|
-
- Use \`{ relationship: { id: "record_id" } }\` or \`{ relationship: { path: "class_record_id" } }\` when matching a known related record.
|
|
18862
|
-
- 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\`.
|
|
18863
|
-
- 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.
|
|
18864
|
-
- Use \`{ relationship: { is: { field: { equal_to: value } } } }\` only for nested field filters. Never put \`id\` or \`path\` inside \`is\`.
|
|
18865
|
-
- 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.
|
|
18866
|
-
- Do not pass a full record instance into a filter; if you already fetched a record, filter by its id or path instead.
|
|
20157
|
+
- Use the generated filter type as the authority: \`OneRelationFilter\` supports \`id\`, \`path\`, \`is\`, \`null\`, \`not_null\`; \`ManyRelationFilter\` supports those plus \`some\`.
|
|
20158
|
+
- Use \`id\` or \`path\` for a known related record; use \`is\` or \`some\` only for nested target-field filters.
|
|
20159
|
+
- 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.
|
|
20160
|
+
- Never pass a full record instance into a filter. Use its id/path or a declared relationship getter.
|
|
18867
20161
|
${domainSections.docs ? `
|
|
18868
20162
|
Domain notes:
|
|
18869
20163
|
${domainSections.docs}
|
|
@@ -18874,6 +20168,24 @@ ${actionIndex}
|
|
|
18874
20168
|
- 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.
|
|
18875
20169
|
- 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(...)\`.
|
|
18876
20170
|
- Actions listed under "Class-level" are class/static methods. Call them on the imported class, e.g. \`await Item.action_name(...)\`.
|
|
20171
|
+
- 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.
|
|
20172
|
+
- For pure field-collection requests, target the class-level entry state handle; for submit/review requests, target the nearest requested later state.
|
|
20173
|
+
- Choose the nearest target state that matches the user's words. Do not aim at a later state just because it is reachable.
|
|
20174
|
+
- 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\`.
|
|
20175
|
+
- 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.
|
|
20176
|
+
- 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.
|
|
20177
|
+
- 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()\`.
|
|
20178
|
+
- 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.
|
|
20179
|
+
- 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.
|
|
20180
|
+
- 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.
|
|
20181
|
+
- 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.
|
|
20182
|
+
- 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.
|
|
20183
|
+
- 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.
|
|
20184
|
+
- 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.
|
|
20185
|
+
- 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.
|
|
20186
|
+
- 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.
|
|
20187
|
+
- 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.
|
|
20188
|
+
- Use \`await prepared.show()\` or \`await actions.show(prepared)\` only to display an already-created prepared action again.
|
|
18877
20189
|
- 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.
|
|
18878
20190
|
- Never call a record-level action as \`Class.action_name(...)\`; that method will not exist.
|
|
18879
20191
|
- 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.
|
|
@@ -18904,6 +20216,8 @@ ${loopBlock}
|
|
|
18904
20216
|
|
|
18905
20217
|
${knownFactsBlock}
|
|
18906
20218
|
|
|
20219
|
+
${manualActionBlock}
|
|
20220
|
+
|
|
18907
20221
|
[Request]
|
|
18908
20222
|
${input.request?.trim() || "Use the latest user message in the conversation."}`;
|
|
18909
20223
|
}
|
|
@@ -19097,8 +20411,9 @@ function buildContinuationInstructionFromTemplate(resultPreview, options) {
|
|
|
19097
20411
|
}
|
|
19098
20412
|
|
|
19099
20413
|
// src/openai-usage.ts
|
|
19100
|
-
var
|
|
19101
|
-
var
|
|
20414
|
+
var OPENAI_GPT_5_4_PRICING_SOURCE_URL = "https://developers.openai.com/api/docs/models/gpt-5.4/";
|
|
20415
|
+
var OPENAI_GPT_5_6_PRICING_SOURCE_URL = "https://developers.openai.com/api/docs/pricing";
|
|
20416
|
+
var OPENAI_LONG_CONTEXT_THRESHOLD_TOKENS = 272e3;
|
|
19102
20417
|
var OPENAI_MODEL_PRICING_USD_PER_MILLION = {
|
|
19103
20418
|
"gpt-5.4": {
|
|
19104
20419
|
provider: "openai",
|
|
@@ -19107,8 +20422,26 @@ var OPENAI_MODEL_PRICING_USD_PER_MILLION = {
|
|
|
19107
20422
|
inputUsdPerMillion: 2.5,
|
|
19108
20423
|
cachedInputUsdPerMillion: 0.25,
|
|
19109
20424
|
outputUsdPerMillion: 15,
|
|
19110
|
-
sourceUrl:
|
|
19111
|
-
effectiveDate:
|
|
20425
|
+
sourceUrl: OPENAI_GPT_5_4_PRICING_SOURCE_URL,
|
|
20426
|
+
effectiveDate: "2026-05-19"
|
|
20427
|
+
},
|
|
20428
|
+
"gpt-5.6-luna": {
|
|
20429
|
+
provider: "openai",
|
|
20430
|
+
model: "gpt-5.6-luna",
|
|
20431
|
+
currency: "USD",
|
|
20432
|
+
inputUsdPerMillion: 1,
|
|
20433
|
+
cachedInputUsdPerMillion: 0.1,
|
|
20434
|
+
cacheWriteUsdPerMillion: 1.25,
|
|
20435
|
+
outputUsdPerMillion: 6,
|
|
20436
|
+
sourceUrl: OPENAI_GPT_5_6_PRICING_SOURCE_URL,
|
|
20437
|
+
effectiveDate: "2026-07-11",
|
|
20438
|
+
longContextThresholdTokens: OPENAI_LONG_CONTEXT_THRESHOLD_TOKENS,
|
|
20439
|
+
longContextPricing: {
|
|
20440
|
+
inputUsdPerMillion: 2,
|
|
20441
|
+
cachedInputUsdPerMillion: 0.2,
|
|
20442
|
+
cacheWriteUsdPerMillion: 2.5,
|
|
20443
|
+
outputUsdPerMillion: 9
|
|
20444
|
+
}
|
|
19112
20445
|
}
|
|
19113
20446
|
};
|
|
19114
20447
|
function asRecord5(value) {
|
|
@@ -19121,8 +20454,18 @@ function numberField(record, key) {
|
|
|
19121
20454
|
function microsPerMillion(usdPerMillion) {
|
|
19122
20455
|
return Math.round(usdPerMillion * 1e6);
|
|
19123
20456
|
}
|
|
19124
|
-
function getOpenAIModelPricing(model) {
|
|
19125
|
-
|
|
20457
|
+
function getOpenAIModelPricing(model, inputTokens = 0) {
|
|
20458
|
+
const pricing = OPENAI_MODEL_PRICING_USD_PER_MILLION[model];
|
|
20459
|
+
if (!pricing) return null;
|
|
20460
|
+
const threshold = pricing.longContextThresholdTokens ?? null;
|
|
20461
|
+
if (pricing.longContextPricing && typeof threshold === "number" && inputTokens > threshold) {
|
|
20462
|
+
return {
|
|
20463
|
+
...pricing,
|
|
20464
|
+
...pricing.longContextPricing,
|
|
20465
|
+
contextTier: "long"
|
|
20466
|
+
};
|
|
20467
|
+
}
|
|
20468
|
+
return { ...pricing, contextTier: "short" };
|
|
19126
20469
|
}
|
|
19127
20470
|
function normalizeOpenAIUsage(rawUsage) {
|
|
19128
20471
|
const usage = asRecord5(rawUsage);
|
|
@@ -19130,6 +20473,7 @@ function normalizeOpenAIUsage(rawUsage) {
|
|
|
19130
20473
|
return {
|
|
19131
20474
|
inputTokens: 0,
|
|
19132
20475
|
cachedInputTokens: 0,
|
|
20476
|
+
cacheWriteTokens: 0,
|
|
19133
20477
|
uncachedInputTokens: 0,
|
|
19134
20478
|
outputTokens: 0,
|
|
19135
20479
|
reasoningTokens: 0,
|
|
@@ -19145,20 +20489,28 @@ function normalizeOpenAIUsage(rawUsage) {
|
|
|
19145
20489
|
inputTokens,
|
|
19146
20490
|
numberField(inputDetails, "cached_tokens") || numberField(inputDetails, "cached_input_tokens")
|
|
19147
20491
|
);
|
|
20492
|
+
const cacheWriteTokens = Math.min(
|
|
20493
|
+
Math.max(inputTokens - cachedInputTokens, 0),
|
|
20494
|
+
numberField(inputDetails, "cache_write_tokens")
|
|
20495
|
+
);
|
|
19148
20496
|
const reasoningTokens = numberField(outputDetails, "reasoning_tokens") || numberField(outputDetails, "reasoning_output_tokens");
|
|
19149
20497
|
return {
|
|
19150
20498
|
inputTokens,
|
|
19151
20499
|
cachedInputTokens,
|
|
19152
|
-
|
|
20500
|
+
cacheWriteTokens,
|
|
20501
|
+
uncachedInputTokens: Math.max(
|
|
20502
|
+
inputTokens - cachedInputTokens - cacheWriteTokens,
|
|
20503
|
+
0
|
|
20504
|
+
),
|
|
19153
20505
|
outputTokens,
|
|
19154
20506
|
reasoningTokens,
|
|
19155
20507
|
totalTokens
|
|
19156
20508
|
};
|
|
19157
20509
|
}
|
|
19158
20510
|
function calculateOpenAITokenSpend(model, rawUsage) {
|
|
19159
|
-
const pricing = getOpenAIModelPricing(model);
|
|
19160
|
-
if (!pricing) return null;
|
|
19161
20511
|
const usage = normalizeOpenAIUsage(rawUsage);
|
|
20512
|
+
const pricing = getOpenAIModelPricing(model, usage.inputTokens);
|
|
20513
|
+
if (!pricing) return null;
|
|
19162
20514
|
const inputPricePerMillionMicros = microsPerMillion(
|
|
19163
20515
|
pricing.inputUsdPerMillion
|
|
19164
20516
|
);
|
|
@@ -19168,14 +20520,19 @@ function calculateOpenAITokenSpend(model, rawUsage) {
|
|
|
19168
20520
|
const outputPricePerMillionMicros = microsPerMillion(
|
|
19169
20521
|
pricing.outputUsdPerMillion
|
|
19170
20522
|
);
|
|
20523
|
+
const cacheWritePricePerMillionMicros = typeof pricing.cacheWriteUsdPerMillion === "number" ? microsPerMillion(pricing.cacheWriteUsdPerMillion) : null;
|
|
20524
|
+
const cacheWriteCostMicros = Math.round(
|
|
20525
|
+
usage.cacheWriteTokens * (cacheWritePricePerMillionMicros ?? inputPricePerMillionMicros) / 1e6
|
|
20526
|
+
);
|
|
19171
20527
|
const amountMicros = Math.round(
|
|
19172
|
-
(usage.uncachedInputTokens * inputPricePerMillionMicros + usage.cachedInputTokens * cachedInputPricePerMillionMicros + usage.outputTokens * outputPricePerMillionMicros) / 1e6
|
|
20528
|
+
(usage.uncachedInputTokens * inputPricePerMillionMicros + usage.cachedInputTokens * cachedInputPricePerMillionMicros + usage.cacheWriteTokens * (cacheWritePricePerMillionMicros ?? inputPricePerMillionMicros) + usage.outputTokens * outputPricePerMillionMicros) / 1e6
|
|
19173
20529
|
);
|
|
19174
20530
|
return {
|
|
19175
20531
|
provider: "openai",
|
|
19176
20532
|
model,
|
|
19177
20533
|
inputTokens: usage.inputTokens,
|
|
19178
20534
|
cachedInputTokens: usage.cachedInputTokens,
|
|
20535
|
+
cacheWriteTokens: usage.cacheWriteTokens,
|
|
19179
20536
|
uncachedInputTokens: usage.uncachedInputTokens,
|
|
19180
20537
|
outputTokens: usage.outputTokens,
|
|
19181
20538
|
reasoningTokens: usage.reasoningTokens,
|
|
@@ -19184,7 +20541,11 @@ function calculateOpenAITokenSpend(model, rawUsage) {
|
|
|
19184
20541
|
currency: "USD",
|
|
19185
20542
|
inputPricePerMillionMicros,
|
|
19186
20543
|
cachedInputPricePerMillionMicros,
|
|
20544
|
+
cacheWritePricePerMillionMicros,
|
|
20545
|
+
cacheWriteCostMicros,
|
|
19187
20546
|
outputPricePerMillionMicros,
|
|
20547
|
+
pricingContextTier: pricing.contextTier || "short",
|
|
20548
|
+
longContextThresholdTokens: pricing.longContextThresholdTokens ?? null,
|
|
19188
20549
|
pricingSource: pricing.sourceUrl,
|
|
19189
20550
|
pricingEffectiveAt: pricing.effectiveDate,
|
|
19190
20551
|
usage
|
|
@@ -19205,6 +20566,8 @@ exports.buildGranularAgentDomainBlock = buildGranularAgentDomainBlock;
|
|
|
19205
20566
|
exports.buildGranularAgentFileBlock = buildGranularAgentFileBlock;
|
|
19206
20567
|
exports.buildGranularAgentHeapBlock = buildGranularAgentHeapBlock;
|
|
19207
20568
|
exports.buildGranularAgentLoopBlock = buildGranularAgentLoopBlock;
|
|
20569
|
+
exports.buildGranularAgentManualActionBlock = buildGranularAgentManualActionBlock;
|
|
20570
|
+
exports.buildGranularAgentManualActionMemorySummary = buildGranularAgentManualActionMemorySummary;
|
|
19208
20571
|
exports.buildGranularAgentReferentBlock = buildGranularAgentReferentBlock;
|
|
19209
20572
|
exports.buildGranularAgentRuntimeImportsBlock = buildGranularAgentRuntimeImportsBlock;
|
|
19210
20573
|
exports.buildGranularAgentSessionBlock = buildGranularAgentSessionBlock;
|
|
@@ -19219,6 +20582,7 @@ exports.consumeGranularReasoningOnlyChunk = consumeGranularReasoningOnlyChunk;
|
|
|
19219
20582
|
exports.consumeGranularReasoningTraceChunk = consumeGranularReasoningTraceChunk;
|
|
19220
20583
|
exports.createHarnessVerifierSnapshot = createHarnessVerifierSnapshot;
|
|
19221
20584
|
exports.evaluateContinuation = evaluateContinuation;
|
|
20585
|
+
exports.evaluateValidationRule = evaluateValidationRule;
|
|
19222
20586
|
exports.extractPromptTokens = extractPromptTokens;
|
|
19223
20587
|
exports.getCurrentClosureId = getCurrentClosureId;
|
|
19224
20588
|
exports.getDefaultHarnessTemplateId = getDefaultHarnessTemplateId;
|
|
@@ -19255,5 +20619,6 @@ exports.scorePromptChoiceMatch = scorePromptChoiceMatch;
|
|
|
19255
20619
|
exports.stripGranularReasoningTrace = stripGranularReasoningTrace;
|
|
19256
20620
|
exports.toGranularHttpBase = toGranularHttpBase;
|
|
19257
20621
|
exports.validateHarnessTemplateManifest = validateHarnessTemplateManifest;
|
|
20622
|
+
exports.validationRuleFailureMessage = validationRuleFailureMessage;
|
|
19258
20623
|
//# sourceMappingURL=index.js.map
|
|
19259
20624
|
//# sourceMappingURL=index.js.map
|