@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.mjs
CHANGED
|
@@ -22,11 +22,11 @@ var __export = (target, all) => {
|
|
|
22
22
|
for (var name in all)
|
|
23
23
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
24
24
|
};
|
|
25
|
-
var __copyProps = (to,
|
|
26
|
-
if (
|
|
27
|
-
for (let key of __getOwnPropNames(
|
|
25
|
+
var __copyProps = (to, from2, except, desc) => {
|
|
26
|
+
if (from2 && typeof from2 === "object" || typeof from2 === "function") {
|
|
27
|
+
for (let key of __getOwnPropNames(from2))
|
|
28
28
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
29
|
-
__defProp(to, key, { get: () =>
|
|
29
|
+
__defProp(to, key, { get: () => from2[key], enumerable: !(desc = __getOwnPropDesc(from2, key)) || desc.enumerable });
|
|
30
30
|
}
|
|
31
31
|
return to;
|
|
32
32
|
};
|
|
@@ -4016,6 +4016,9 @@ function rpcTimeoutMsForMethod(method) {
|
|
|
4016
4016
|
return DOMAIN_PACKAGE_RPC_TIMEOUT_MS;
|
|
4017
4017
|
case "client.heartbeat":
|
|
4018
4018
|
case "effects.publishCatalog":
|
|
4019
|
+
case "effects.resetCatalog":
|
|
4020
|
+
case "effects.addCatalog":
|
|
4021
|
+
case "effects.removeCatalog":
|
|
4019
4022
|
case "effects.refresh":
|
|
4020
4023
|
return EFFECT_CONTROL_RPC_TIMEOUT_MS;
|
|
4021
4024
|
case "harness.run":
|
|
@@ -4040,16 +4043,37 @@ var WSClient = class {
|
|
|
4040
4043
|
tokenRefreshTimer = null;
|
|
4041
4044
|
isExplicitlyDisconnected = false;
|
|
4042
4045
|
reconnectAttempts = 0;
|
|
4046
|
+
connectPromise = null;
|
|
4047
|
+
connectionEpoch = 0;
|
|
4048
|
+
cancelConnectAttempt = null;
|
|
4043
4049
|
options;
|
|
4044
4050
|
constructor(options) {
|
|
4045
4051
|
this.options = options;
|
|
4046
4052
|
this.url = options.url;
|
|
4047
4053
|
this.sessionId = options.sessionId;
|
|
4048
4054
|
this.token = options.token;
|
|
4055
|
+
if (options.initialDocumentSnapshot) {
|
|
4056
|
+
this.seedDocumentSnapshot(options.initialDocumentSnapshot);
|
|
4057
|
+
}
|
|
4049
4058
|
}
|
|
4050
4059
|
get currentSessionId() {
|
|
4051
4060
|
return this.sessionId;
|
|
4052
4061
|
}
|
|
4062
|
+
seedDocumentSnapshot(document) {
|
|
4063
|
+
if (!document || typeof document !== "object" || Array.isArray(document)) {
|
|
4064
|
+
return;
|
|
4065
|
+
}
|
|
4066
|
+
try {
|
|
4067
|
+
this.doc = document instanceof Uint8Array ? Automerge.load(document) : Automerge.from(document);
|
|
4068
|
+
this.syncState = Automerge.initSyncState();
|
|
4069
|
+
this.emit("sync", this.doc);
|
|
4070
|
+
} catch (error) {
|
|
4071
|
+
console.warn("[Granular] Failed to seed cached session document", error);
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
saveDocumentSnapshot() {
|
|
4075
|
+
return Automerge.save(this.doc);
|
|
4076
|
+
}
|
|
4053
4077
|
clearTokenRefreshTimer() {
|
|
4054
4078
|
if (this.tokenRefreshTimer) {
|
|
4055
4079
|
clearTimeout(this.tokenRefreshTimer);
|
|
@@ -4159,8 +4183,23 @@ var WSClient = class {
|
|
|
4159
4183
|
* Connect to the WebSocket server
|
|
4160
4184
|
* @returns {Promise<void>} Resolves when connection is open
|
|
4161
4185
|
*/
|
|
4162
|
-
async connect() {
|
|
4186
|
+
async connect(options = {}) {
|
|
4187
|
+
if (this.ws?.readyState === READY_STATE_OPEN) return;
|
|
4188
|
+
if (this.connectPromise) return this.connectPromise;
|
|
4189
|
+
const connectPromise = this.connectAttempt(options.signal);
|
|
4190
|
+
this.connectPromise = connectPromise;
|
|
4191
|
+
try {
|
|
4192
|
+
await connectPromise;
|
|
4193
|
+
} finally {
|
|
4194
|
+
if (this.connectPromise === connectPromise) {
|
|
4195
|
+
this.connectPromise = null;
|
|
4196
|
+
}
|
|
4197
|
+
}
|
|
4198
|
+
}
|
|
4199
|
+
async connectAttempt(signal) {
|
|
4200
|
+
if (signal?.aborted) throw new Error("WebSocket connect aborted");
|
|
4163
4201
|
const token = await this.resolveTokenForConnect();
|
|
4202
|
+
if (signal?.aborted) throw new Error("WebSocket connect aborted");
|
|
4164
4203
|
this.isExplicitlyDisconnected = false;
|
|
4165
4204
|
this.scheduleTokenRefresh();
|
|
4166
4205
|
if (this.reconnectTimer) {
|
|
@@ -4172,7 +4211,7 @@ var WSClient = class {
|
|
|
4172
4211
|
try {
|
|
4173
4212
|
const wsModule = await Promise.resolve().then(() => (init_wrapper(), wrapper_exports));
|
|
4174
4213
|
WebSocketClass = wsModule.default || wsModule;
|
|
4175
|
-
} catch
|
|
4214
|
+
} catch {
|
|
4176
4215
|
}
|
|
4177
4216
|
}
|
|
4178
4217
|
if (!WebSocketClass) {
|
|
@@ -4180,83 +4219,97 @@ var WSClient = class {
|
|
|
4180
4219
|
'No WebSocket implementation found. If using Node.js, please install "ws" and pass the constructor to the SDK options: { WebSocketCtor: WebSocket }.'
|
|
4181
4220
|
);
|
|
4182
4221
|
}
|
|
4222
|
+
const epoch = ++this.connectionEpoch;
|
|
4223
|
+
const wsUrl = new URL(this.url);
|
|
4224
|
+
wsUrl.searchParams.set("sessionId", this.sessionId);
|
|
4225
|
+
wsUrl.searchParams.set("token", token);
|
|
4226
|
+
const socket = new WebSocketClass(wsUrl.toString());
|
|
4227
|
+
this.ws = socket;
|
|
4183
4228
|
return new Promise((resolve, reject) => {
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
if (
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
this.reconnectTimer = null;
|
|
4196
|
-
}
|
|
4197
|
-
this.reconnectAttempts = 0;
|
|
4198
|
-
this.emit("open", {});
|
|
4199
|
-
resolve();
|
|
4200
|
-
});
|
|
4201
|
-
socket.on("message", (data) => {
|
|
4202
|
-
try {
|
|
4203
|
-
const message = JSON.parse(data.toString());
|
|
4204
|
-
this.handleMessage(message);
|
|
4205
|
-
} catch (error) {
|
|
4206
|
-
console.error("[Granular] Failed to parse message:", error);
|
|
4207
|
-
}
|
|
4208
|
-
});
|
|
4209
|
-
socket.on("error", (error) => {
|
|
4210
|
-
this.emit("error", error);
|
|
4211
|
-
if (socket.readyState !== READY_STATE_OPEN) {
|
|
4212
|
-
reject(error);
|
|
4213
|
-
}
|
|
4214
|
-
});
|
|
4215
|
-
socket.on("close", (code, reason) => {
|
|
4216
|
-
this.handleDisconnect({
|
|
4217
|
-
code,
|
|
4218
|
-
reason: this.normalizeReason(reason),
|
|
4219
|
-
// ws does not provide wasClean on Node-style close callback
|
|
4220
|
-
wasClean: code === 1e3
|
|
4221
|
-
});
|
|
4222
|
-
});
|
|
4229
|
+
let settled = false;
|
|
4230
|
+
const isCurrent = () => this.connectionEpoch === epoch && this.ws === socket;
|
|
4231
|
+
const finish = (error) => {
|
|
4232
|
+
if (settled) return;
|
|
4233
|
+
settled = true;
|
|
4234
|
+
if (this.cancelConnectAttempt === handleAbort) {
|
|
4235
|
+
this.cancelConnectAttempt = null;
|
|
4236
|
+
}
|
|
4237
|
+
signal?.removeEventListener("abort", handleAbort);
|
|
4238
|
+
if (error) {
|
|
4239
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
4223
4240
|
} else {
|
|
4224
|
-
|
|
4225
|
-
if (this.reconnectTimer) {
|
|
4226
|
-
clearTimeout(this.reconnectTimer);
|
|
4227
|
-
this.reconnectTimer = null;
|
|
4228
|
-
}
|
|
4229
|
-
this.reconnectAttempts = 0;
|
|
4230
|
-
this.emit("open", {});
|
|
4231
|
-
resolve();
|
|
4232
|
-
};
|
|
4233
|
-
this.ws.onmessage = (event) => {
|
|
4234
|
-
try {
|
|
4235
|
-
const data = event.data;
|
|
4236
|
-
const message = JSON.parse(data.toString());
|
|
4237
|
-
this.handleMessage(message);
|
|
4238
|
-
} catch (error) {
|
|
4239
|
-
console.error("[Granular] Failed to parse message:", error);
|
|
4240
|
-
}
|
|
4241
|
-
};
|
|
4242
|
-
this.ws.onerror = (event) => {
|
|
4243
|
-
const error = new Error("WebSocket error");
|
|
4244
|
-
error.event = event;
|
|
4245
|
-
this.emit("error", error);
|
|
4246
|
-
if (this.ws?.readyState !== READY_STATE_OPEN) {
|
|
4247
|
-
reject(error);
|
|
4248
|
-
}
|
|
4249
|
-
};
|
|
4250
|
-
this.ws.onclose = (event) => {
|
|
4251
|
-
this.handleDisconnect({
|
|
4252
|
-
code: event.code,
|
|
4253
|
-
reason: event.reason,
|
|
4254
|
-
wasClean: event.wasClean
|
|
4255
|
-
});
|
|
4256
|
-
};
|
|
4241
|
+
resolve();
|
|
4257
4242
|
}
|
|
4258
|
-
}
|
|
4259
|
-
|
|
4243
|
+
};
|
|
4244
|
+
const closeStaleSocket = () => {
|
|
4245
|
+
try {
|
|
4246
|
+
socket.close(1e3, "Stale connection attempt");
|
|
4247
|
+
} catch {
|
|
4248
|
+
}
|
|
4249
|
+
};
|
|
4250
|
+
const handleAbort = () => {
|
|
4251
|
+
if (isCurrent()) {
|
|
4252
|
+
this.connectionEpoch += 1;
|
|
4253
|
+
this.ws = null;
|
|
4254
|
+
}
|
|
4255
|
+
closeStaleSocket();
|
|
4256
|
+
finish(new Error("WebSocket connect aborted"));
|
|
4257
|
+
};
|
|
4258
|
+
this.cancelConnectAttempt = handleAbort;
|
|
4259
|
+
const handleOpen = () => {
|
|
4260
|
+
if (!isCurrent()) {
|
|
4261
|
+
closeStaleSocket();
|
|
4262
|
+
return;
|
|
4263
|
+
}
|
|
4264
|
+
this.reconnectAttempts = 0;
|
|
4265
|
+
this.emit("open", {});
|
|
4266
|
+
finish();
|
|
4267
|
+
};
|
|
4268
|
+
const handleMessage = (data) => {
|
|
4269
|
+
if (!isCurrent()) return;
|
|
4270
|
+
try {
|
|
4271
|
+
const text = typeof data === "string" ? data : data && typeof data === "object" && "toString" in data ? String(data.toString()) : "";
|
|
4272
|
+
this.handleMessage(JSON.parse(text));
|
|
4273
|
+
} catch (error) {
|
|
4274
|
+
console.error("[Granular] Failed to parse message:", error);
|
|
4275
|
+
}
|
|
4276
|
+
};
|
|
4277
|
+
const handleError = (error) => {
|
|
4278
|
+
if (!isCurrent()) return;
|
|
4279
|
+
const typedError = error instanceof Error ? error : new Error("WebSocket error");
|
|
4280
|
+
this.emit("error", typedError);
|
|
4281
|
+
if (socket.readyState !== READY_STATE_OPEN) finish(typedError);
|
|
4282
|
+
};
|
|
4283
|
+
const handleClose = (close) => {
|
|
4284
|
+
if (!isCurrent()) return;
|
|
4285
|
+
if (!settled) {
|
|
4286
|
+
finish(
|
|
4287
|
+
new Error(
|
|
4288
|
+
`WebSocket closed before ready${close.code ? ` (code=${close.code})` : ""}`
|
|
4289
|
+
)
|
|
4290
|
+
);
|
|
4291
|
+
}
|
|
4292
|
+
this.handleDisconnect({
|
|
4293
|
+
code: close.code,
|
|
4294
|
+
reason: this.normalizeReason(close.reason),
|
|
4295
|
+
wasClean: close.wasClean
|
|
4296
|
+
});
|
|
4297
|
+
};
|
|
4298
|
+
signal?.addEventListener("abort", handleAbort, { once: true });
|
|
4299
|
+
const nodeSocket = socket;
|
|
4300
|
+
if (typeof nodeSocket.on === "function") {
|
|
4301
|
+
nodeSocket.on("open", handleOpen);
|
|
4302
|
+
nodeSocket.on("message", handleMessage);
|
|
4303
|
+
nodeSocket.on("error", handleError);
|
|
4304
|
+
nodeSocket.on(
|
|
4305
|
+
"close",
|
|
4306
|
+
(code, reason) => handleClose({ code, reason, wasClean: code === 1e3 })
|
|
4307
|
+
);
|
|
4308
|
+
} else {
|
|
4309
|
+
socket.onopen = handleOpen;
|
|
4310
|
+
socket.onmessage = (event) => handleMessage(event.data);
|
|
4311
|
+
socket.onerror = handleError;
|
|
4312
|
+
socket.onclose = (event) => handleClose(event);
|
|
4260
4313
|
}
|
|
4261
4314
|
});
|
|
4262
4315
|
}
|
|
@@ -4274,9 +4327,58 @@ var WSClient = class {
|
|
|
4274
4327
|
return void 0;
|
|
4275
4328
|
}
|
|
4276
4329
|
rejectPending(error) {
|
|
4277
|
-
this.messageQueue.forEach((pending) =>
|
|
4330
|
+
this.messageQueue.forEach((pending) => {
|
|
4331
|
+
clearTimeout(pending.timeout);
|
|
4332
|
+
pending.reject(error);
|
|
4333
|
+
});
|
|
4278
4334
|
this.messageQueue = [];
|
|
4279
4335
|
}
|
|
4336
|
+
emitReconnectErrorMessage(error) {
|
|
4337
|
+
const reconnectInfo = {
|
|
4338
|
+
error,
|
|
4339
|
+
sessionId: this.sessionId,
|
|
4340
|
+
timestamp: Date.now()
|
|
4341
|
+
};
|
|
4342
|
+
this.emit("reconnect_error", reconnectInfo);
|
|
4343
|
+
if (this.options.onReconnectError) {
|
|
4344
|
+
try {
|
|
4345
|
+
this.options.onReconnectError(reconnectInfo);
|
|
4346
|
+
} catch (callbackError) {
|
|
4347
|
+
console.error(
|
|
4348
|
+
"[Granular] onReconnectError callback failed:",
|
|
4349
|
+
callbackError
|
|
4350
|
+
);
|
|
4351
|
+
}
|
|
4352
|
+
}
|
|
4353
|
+
}
|
|
4354
|
+
scheduleReconnectAttempt() {
|
|
4355
|
+
if (this.isExplicitlyDisconnected || this.reconnectTimer) return null;
|
|
4356
|
+
const baseReconnectDelayMs = typeof this.options.reconnectDelayMs === "number" && Number.isFinite(this.options.reconnectDelayMs) && this.options.reconnectDelayMs > 0 ? this.options.reconnectDelayMs : DEFAULT_RECONNECT_DELAY_MS;
|
|
4357
|
+
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;
|
|
4358
|
+
if (this.reconnectAttempts >= maxReconnectAttempts) {
|
|
4359
|
+
this.emitReconnectErrorMessage(
|
|
4360
|
+
`WebSocket reconnect attempts exhausted after ${maxReconnectAttempts} attempt(s).`
|
|
4361
|
+
);
|
|
4362
|
+
return null;
|
|
4363
|
+
}
|
|
4364
|
+
this.reconnectAttempts += 1;
|
|
4365
|
+
const reconnectDelayMs = Math.min(
|
|
4366
|
+
3e4,
|
|
4367
|
+
baseReconnectDelayMs * 2 ** Math.max(0, this.reconnectAttempts - 1)
|
|
4368
|
+
);
|
|
4369
|
+
this.reconnectTimer = setTimeout(() => {
|
|
4370
|
+
this.reconnectTimer = null;
|
|
4371
|
+
console.log("[Granular] Attempting reconnect...");
|
|
4372
|
+
this.connect().catch((error) => {
|
|
4373
|
+
console.error("[Granular] Reconnect failed:", error);
|
|
4374
|
+
this.emitReconnectErrorMessage(
|
|
4375
|
+
error instanceof Error ? error.message : String(error)
|
|
4376
|
+
);
|
|
4377
|
+
this.scheduleReconnectAttempt();
|
|
4378
|
+
});
|
|
4379
|
+
}, reconnectDelayMs);
|
|
4380
|
+
return reconnectDelayMs;
|
|
4381
|
+
}
|
|
4280
4382
|
buildDisconnectError(info) {
|
|
4281
4383
|
const details = [
|
|
4282
4384
|
info.code !== void 0 ? `code=${info.code}` : void 0,
|
|
@@ -4286,8 +4388,6 @@ var WSClient = class {
|
|
|
4286
4388
|
return new Error(`WebSocket disconnected${suffix}`);
|
|
4287
4389
|
}
|
|
4288
4390
|
handleDisconnect(close = {}) {
|
|
4289
|
-
const baseReconnectDelayMs = typeof this.options.reconnectDelayMs === "number" && Number.isFinite(this.options.reconnectDelayMs) && this.options.reconnectDelayMs > 0 ? this.options.reconnectDelayMs : DEFAULT_RECONNECT_DELAY_MS;
|
|
4290
|
-
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;
|
|
4291
4391
|
const unexpected = !this.isExplicitlyDisconnected;
|
|
4292
4392
|
const info = {
|
|
4293
4393
|
code: close.code,
|
|
@@ -4307,32 +4407,9 @@ var WSClient = class {
|
|
|
4307
4407
|
const disconnectError = this.buildDisconnectError(info);
|
|
4308
4408
|
this.rejectPending(disconnectError);
|
|
4309
4409
|
this.emit("disconnect", info);
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
sessionId: this.sessionId,
|
|
4314
|
-
timestamp: Date.now()
|
|
4315
|
-
};
|
|
4316
|
-
this.emit("reconnect_error", reconnectInfo);
|
|
4317
|
-
if (this.options.onReconnectError) {
|
|
4318
|
-
try {
|
|
4319
|
-
this.options.onReconnectError(reconnectInfo);
|
|
4320
|
-
} catch (callbackError) {
|
|
4321
|
-
console.error(
|
|
4322
|
-
"[Granular] onReconnectError callback failed:",
|
|
4323
|
-
callbackError
|
|
4324
|
-
);
|
|
4325
|
-
}
|
|
4326
|
-
}
|
|
4327
|
-
return;
|
|
4328
|
-
}
|
|
4329
|
-
this.reconnectAttempts += 1;
|
|
4330
|
-
const reconnectDelayMs = Math.min(
|
|
4331
|
-
3e4,
|
|
4332
|
-
baseReconnectDelayMs * 2 ** Math.max(0, this.reconnectAttempts - 1)
|
|
4333
|
-
);
|
|
4334
|
-
info.reconnectScheduled = true;
|
|
4335
|
-
info.reconnectDelayMs = reconnectDelayMs;
|
|
4410
|
+
const reconnectDelayMs = this.scheduleReconnectAttempt();
|
|
4411
|
+
info.reconnectScheduled = reconnectDelayMs !== null;
|
|
4412
|
+
if (reconnectDelayMs !== null) info.reconnectDelayMs = reconnectDelayMs;
|
|
4336
4413
|
if (this.options.onUnexpectedClose) {
|
|
4337
4414
|
try {
|
|
4338
4415
|
this.options.onUnexpectedClose(info);
|
|
@@ -4343,28 +4420,6 @@ var WSClient = class {
|
|
|
4343
4420
|
);
|
|
4344
4421
|
}
|
|
4345
4422
|
}
|
|
4346
|
-
this.reconnectTimer = setTimeout(() => {
|
|
4347
|
-
console.log("[Granular] Attempting reconnect...");
|
|
4348
|
-
this.connect().catch((error) => {
|
|
4349
|
-
console.error("[Granular] Reconnect failed:", error);
|
|
4350
|
-
const reconnectInfo = {
|
|
4351
|
-
error: error instanceof Error ? error.message : String(error),
|
|
4352
|
-
sessionId: this.sessionId,
|
|
4353
|
-
timestamp: Date.now()
|
|
4354
|
-
};
|
|
4355
|
-
this.emit("reconnect_error", reconnectInfo);
|
|
4356
|
-
if (this.options.onReconnectError) {
|
|
4357
|
-
try {
|
|
4358
|
-
this.options.onReconnectError(reconnectInfo);
|
|
4359
|
-
} catch (callbackError) {
|
|
4360
|
-
console.error(
|
|
4361
|
-
"[Granular] onReconnectError callback failed:",
|
|
4362
|
-
callbackError
|
|
4363
|
-
);
|
|
4364
|
-
}
|
|
4365
|
-
}
|
|
4366
|
-
});
|
|
4367
|
-
}, reconnectDelayMs);
|
|
4368
4423
|
}
|
|
4369
4424
|
}
|
|
4370
4425
|
handleMessage(message) {
|
|
@@ -4475,6 +4530,7 @@ var WSClient = class {
|
|
|
4475
4530
|
const response = message;
|
|
4476
4531
|
const pending = this.messageQueue.find((q) => q.id === response.id);
|
|
4477
4532
|
if (pending) {
|
|
4533
|
+
clearTimeout(pending.timeout);
|
|
4478
4534
|
if (response.type === "rpc_error") {
|
|
4479
4535
|
pending.reject(
|
|
4480
4536
|
new Error(
|
|
@@ -4520,16 +4576,22 @@ var WSClient = class {
|
|
|
4520
4576
|
id
|
|
4521
4577
|
};
|
|
4522
4578
|
return new Promise((resolve, reject) => {
|
|
4523
|
-
this.messageQueue.push({ resolve, reject, id });
|
|
4524
|
-
this.ws.send(JSON.stringify(request));
|
|
4525
4579
|
const timeoutMs = rpcTimeoutMsForMethod(method);
|
|
4526
|
-
setTimeout(() => {
|
|
4580
|
+
const timeout = setTimeout(() => {
|
|
4527
4581
|
const pending = this.messageQueue.find((q) => q.id === id);
|
|
4528
4582
|
if (pending) {
|
|
4529
4583
|
this.messageQueue = this.messageQueue.filter((q) => q.id !== id);
|
|
4530
4584
|
reject(new Error(`RPC timeout: ${method}`));
|
|
4531
4585
|
}
|
|
4532
4586
|
}, timeoutMs);
|
|
4587
|
+
this.messageQueue.push({ resolve, reject, id, timeout });
|
|
4588
|
+
try {
|
|
4589
|
+
this.ws.send(JSON.stringify(request));
|
|
4590
|
+
} catch (error) {
|
|
4591
|
+
clearTimeout(timeout);
|
|
4592
|
+
this.messageQueue = this.messageQueue.filter((q) => q.id !== id);
|
|
4593
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
4594
|
+
}
|
|
4533
4595
|
});
|
|
4534
4596
|
}
|
|
4535
4597
|
async handleIncomingRpc(request) {
|
|
@@ -4615,15 +4677,18 @@ var WSClient = class {
|
|
|
4615
4677
|
/**
|
|
4616
4678
|
* Disconnect the WebSocket and clear state
|
|
4617
4679
|
*/
|
|
4618
|
-
disconnect() {
|
|
4680
|
+
disconnect(options = {}) {
|
|
4619
4681
|
this.isExplicitlyDisconnected = true;
|
|
4682
|
+
this.cancelConnectAttempt?.();
|
|
4683
|
+
this.cancelConnectAttempt = null;
|
|
4684
|
+
this.connectionEpoch += 1;
|
|
4620
4685
|
if (this.reconnectTimer) {
|
|
4621
4686
|
clearTimeout(this.reconnectTimer);
|
|
4622
4687
|
this.reconnectTimer = null;
|
|
4623
4688
|
}
|
|
4624
4689
|
this.clearTokenRefreshTimer();
|
|
4625
4690
|
if (this.ws) {
|
|
4626
|
-
this.ws.close(1e3, "Client disconnect");
|
|
4691
|
+
this.ws.close(1e3, options.reason || "Client disconnect");
|
|
4627
4692
|
this.ws = null;
|
|
4628
4693
|
}
|
|
4629
4694
|
this.rejectPending(new Error("Client explicitly disconnected"));
|
|
@@ -4719,8 +4784,12 @@ function normalizePrompt(rawValue) {
|
|
|
4719
4784
|
const source = promptRecord || raw;
|
|
4720
4785
|
const id = typeof source.id === "string" ? source.id : typeof raw.id === "string" ? raw.id : typeof raw.promptId === "string" ? raw.promptId : "";
|
|
4721
4786
|
if (!id) return null;
|
|
4787
|
+
const jobId = typeof source.jobId === "string" && source.jobId.trim() ? source.jobId.trim() : typeof raw.jobId === "string" && raw.jobId.trim() ? raw.jobId.trim() : void 0;
|
|
4788
|
+
const turnId = typeof source.turnId === "string" && source.turnId.trim() ? source.turnId.trim() : typeof raw.turnId === "string" && raw.turnId.trim() ? raw.turnId.trim() : void 0;
|
|
4722
4789
|
return {
|
|
4723
4790
|
id,
|
|
4791
|
+
...jobId ? { jobId } : {},
|
|
4792
|
+
...turnId ? { turnId } : {},
|
|
4724
4793
|
type: normalizePromptType(source === raw ? raw : { ...raw, ...source }),
|
|
4725
4794
|
title: typeof source.title === "string" ? source.title : "Input required",
|
|
4726
4795
|
message: typeof source.message === "string" ? source.message : "",
|
|
@@ -4761,6 +4830,9 @@ function resolvePromptAnswer(prompt, answer) {
|
|
|
4761
4830
|
|
|
4762
4831
|
// src/session.ts
|
|
4763
4832
|
var PROMPT_TRANSCRIPT_APPEND_TIMEOUT_MS = 5e3;
|
|
4833
|
+
function toPascalCase(value) {
|
|
4834
|
+
return value.split(/[_:\-\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
4835
|
+
}
|
|
4764
4836
|
function withPromptTranscriptTimeout(promise) {
|
|
4765
4837
|
let timeout = null;
|
|
4766
4838
|
return Promise.race([
|
|
@@ -4804,6 +4876,9 @@ var Session = class {
|
|
|
4804
4876
|
this.initialQuota = options.initialQuota || null;
|
|
4805
4877
|
this.setupEventHandlers();
|
|
4806
4878
|
this.setupToolInvokeHandler();
|
|
4879
|
+
this.currentDomainRevision = this.extractDomainRevisionFromDoc(
|
|
4880
|
+
this.client.doc
|
|
4881
|
+
);
|
|
4807
4882
|
}
|
|
4808
4883
|
extractDomainRevisionFromDoc(doc) {
|
|
4809
4884
|
const domain = doc?.domain;
|
|
@@ -5323,9 +5398,7 @@ var Session = class {
|
|
|
5323
5398
|
if (classes && Object.keys(classes).length > 0) {
|
|
5324
5399
|
let docs2 = "# Domain Documentation\n\n";
|
|
5325
5400
|
docs2 += "Import concrete classes from `@granular/domain/<Class>` and global backend actions from `@granular/actions/backend`:\n\n";
|
|
5326
|
-
const classNames = Object.keys(classes).map(
|
|
5327
|
-
(c) => c.charAt(0).toUpperCase() + c.slice(1)
|
|
5328
|
-
);
|
|
5401
|
+
const classNames = Object.keys(classes).map(toPascalCase);
|
|
5329
5402
|
const globalNames = (globalTools || []).map((t) => t.name);
|
|
5330
5403
|
const importLines = [
|
|
5331
5404
|
...classNames.map(
|
|
@@ -5339,7 +5412,7 @@ ${importLines.join("\n") || "// No generated domain imports available."}
|
|
|
5339
5412
|
|
|
5340
5413
|
`;
|
|
5341
5414
|
for (const [className, cls] of Object.entries(classes)) {
|
|
5342
|
-
const TsName =
|
|
5415
|
+
const TsName = toPascalCase(className);
|
|
5343
5416
|
docs2 += `## ${TsName}
|
|
5344
5417
|
|
|
5345
5418
|
`;
|
|
@@ -5711,6 +5784,7 @@ function normalizeJobAgentMessageEnvelope(data) {
|
|
|
5711
5784
|
}
|
|
5712
5785
|
return {
|
|
5713
5786
|
jobId: d.jobId,
|
|
5787
|
+
...typeof d.turnId === "string" && d.turnId.trim() ? { turnId: d.turnId.trim() } : {},
|
|
5714
5788
|
message: {
|
|
5715
5789
|
messageId: d.messageId,
|
|
5716
5790
|
kind: d.kind === "artifacts" ? "artifacts" : "text",
|
|
@@ -6329,6 +6403,28 @@ function asString(value) {
|
|
|
6329
6403
|
function trimString(value) {
|
|
6330
6404
|
return typeof value === "string" ? value.trim() : "";
|
|
6331
6405
|
}
|
|
6406
|
+
function compactJson(value, maxLength = 320) {
|
|
6407
|
+
if (value === void 0 || value === null) return void 0;
|
|
6408
|
+
try {
|
|
6409
|
+
const json = JSON.stringify(value);
|
|
6410
|
+
if (!json || json === "undefined") return void 0;
|
|
6411
|
+
return json.length > maxLength ? `${json.slice(0, maxLength)}...` : json;
|
|
6412
|
+
} catch {
|
|
6413
|
+
return String(value);
|
|
6414
|
+
}
|
|
6415
|
+
}
|
|
6416
|
+
function artifactRecordsById(liveDoc) {
|
|
6417
|
+
const artifacts = asRecord3(liveDoc?.artifacts);
|
|
6418
|
+
const byId = asRecord3(artifacts?.byId) || {};
|
|
6419
|
+
return Object.fromEntries(
|
|
6420
|
+
Object.entries(byId).map(([artifactId, value]) => {
|
|
6421
|
+
const record = asRecord3(value);
|
|
6422
|
+
return record ? [artifactId, record] : null;
|
|
6423
|
+
}).filter(
|
|
6424
|
+
(entry) => Boolean(entry)
|
|
6425
|
+
)
|
|
6426
|
+
);
|
|
6427
|
+
}
|
|
6332
6428
|
function normalizeShowRefs(value) {
|
|
6333
6429
|
const record = asRecord3(value);
|
|
6334
6430
|
if (!record) return void 0;
|
|
@@ -6345,9 +6441,106 @@ function normalizeShowRefs(value) {
|
|
|
6345
6441
|
entryPaths: normalizeRefs(record.entryPaths),
|
|
6346
6442
|
listNames: normalizeRefs(record.listNames),
|
|
6347
6443
|
variableNames: normalizeRefs(record.variableNames),
|
|
6348
|
-
fileIds: normalizeRefs(record.fileIds)
|
|
6444
|
+
fileIds: normalizeRefs(record.fileIds),
|
|
6445
|
+
sessionArtifactIds: normalizeRefs(record.sessionArtifactIds),
|
|
6446
|
+
actionSuggestions: normalizeActionSuggestions(record.actionSuggestions),
|
|
6447
|
+
tables: Array.isArray(record.tables) ? record.tables.filter(
|
|
6448
|
+
(table) => Boolean(
|
|
6449
|
+
table && typeof table === "object" && !Array.isArray(table) && Array.isArray(table.columns) && Array.isArray(table.rows)
|
|
6450
|
+
)
|
|
6451
|
+
) : void 0
|
|
6349
6452
|
};
|
|
6350
|
-
return show.entryPaths || show.listNames || show.variableNames || show.fileIds ? show : void 0;
|
|
6453
|
+
return show.entryPaths || show.listNames || show.variableNames || show.fileIds || show.sessionArtifactIds || show.actionSuggestions || show.tables ? show : void 0;
|
|
6454
|
+
}
|
|
6455
|
+
function normalizeActionSuggestions(value) {
|
|
6456
|
+
if (!Array.isArray(value)) return void 0;
|
|
6457
|
+
const suggestions = [];
|
|
6458
|
+
for (const item of value) {
|
|
6459
|
+
const record = asRecord3(item);
|
|
6460
|
+
if (!record) continue;
|
|
6461
|
+
const label = trimString(record.label);
|
|
6462
|
+
if (!label) continue;
|
|
6463
|
+
const suggestionId = trimString(record.suggestionId) || trimString(record.id) || label;
|
|
6464
|
+
suggestions.push({
|
|
6465
|
+
suggestionId,
|
|
6466
|
+
label,
|
|
6467
|
+
...typeof record.description === "string" ? { description: record.description } : {},
|
|
6468
|
+
...asRecord3(record.artifact) ? { artifact: asRecord3(record.artifact) } : {},
|
|
6469
|
+
...asRecord3(record.target) ? { target: asRecord3(record.target) } : {},
|
|
6470
|
+
...asRecord3(record.metadata) ? { metadata: asRecord3(record.metadata) } : {}
|
|
6471
|
+
});
|
|
6472
|
+
}
|
|
6473
|
+
return suggestions.length ? suggestions : void 0;
|
|
6474
|
+
}
|
|
6475
|
+
var TRANSCRIPT_MESSAGE_PART_LIMIT = 128;
|
|
6476
|
+
var TRANSCRIPT_MESSAGE_PART_TEXT_LIMIT = 2e5;
|
|
6477
|
+
var TRANSCRIPT_MESSAGE_PART_ACTION_LABEL_LIMIT = 1e3;
|
|
6478
|
+
var TRANSCRIPT_MESSAGE_ACTION_LIMIT = 64;
|
|
6479
|
+
function normalizeConversationMessageActions(value) {
|
|
6480
|
+
if (!Array.isArray(value) || value.length === 0) return void 0;
|
|
6481
|
+
const actions = [];
|
|
6482
|
+
for (const item of value.slice(0, TRANSCRIPT_MESSAGE_ACTION_LIMIT)) {
|
|
6483
|
+
const record = asRecord3(item);
|
|
6484
|
+
const kind = record?.kind;
|
|
6485
|
+
const label = trimString(record?.label ?? record?.title);
|
|
6486
|
+
const status = record?.status;
|
|
6487
|
+
if (kind !== "frontend" && kind !== "backend" && kind !== "system" || !label || label.length > TRANSCRIPT_MESSAGE_PART_ACTION_LABEL_LIMIT) {
|
|
6488
|
+
continue;
|
|
6489
|
+
}
|
|
6490
|
+
actions.push({
|
|
6491
|
+
kind,
|
|
6492
|
+
label,
|
|
6493
|
+
...status === "done" || status === "queued" || status === "failed" ? { status } : {}
|
|
6494
|
+
});
|
|
6495
|
+
}
|
|
6496
|
+
return actions.length ? actions : void 0;
|
|
6497
|
+
}
|
|
6498
|
+
function normalizeConversationMessageParts(value, canonicalContent, canonicalActions) {
|
|
6499
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > TRANSCRIPT_MESSAGE_PART_LIMIT) {
|
|
6500
|
+
return void 0;
|
|
6501
|
+
}
|
|
6502
|
+
const parts = [];
|
|
6503
|
+
const canonicalActionsById = new Map(
|
|
6504
|
+
(canonicalActions || []).map((action) => [
|
|
6505
|
+
`${action.kind}:${action.label}`,
|
|
6506
|
+
action
|
|
6507
|
+
])
|
|
6508
|
+
);
|
|
6509
|
+
const seenActionIds = /* @__PURE__ */ new Set();
|
|
6510
|
+
let textLength = 0;
|
|
6511
|
+
for (const item of value) {
|
|
6512
|
+
const record = asRecord3(item);
|
|
6513
|
+
if (!record) return void 0;
|
|
6514
|
+
if (record.type === "text") {
|
|
6515
|
+
if (typeof record.text !== "string" || record.text.length === 0) {
|
|
6516
|
+
return void 0;
|
|
6517
|
+
}
|
|
6518
|
+
textLength += record.text.length;
|
|
6519
|
+
if (textLength > TRANSCRIPT_MESSAGE_PART_TEXT_LIMIT) return void 0;
|
|
6520
|
+
parts.push({ type: "text", text: record.text });
|
|
6521
|
+
continue;
|
|
6522
|
+
}
|
|
6523
|
+
if (record.type !== "action") return void 0;
|
|
6524
|
+
const action = asRecord3(record.action);
|
|
6525
|
+
const kind = action?.kind;
|
|
6526
|
+
const label = trimString(action?.label);
|
|
6527
|
+
if (kind !== "frontend" && kind !== "backend" && kind !== "system" || !label || label.length > TRANSCRIPT_MESSAGE_PART_ACTION_LABEL_LIMIT) {
|
|
6528
|
+
return void 0;
|
|
6529
|
+
}
|
|
6530
|
+
const actionId = `${kind}:${label}`;
|
|
6531
|
+
const canonicalAction = canonicalActionsById.get(actionId);
|
|
6532
|
+
if (!canonicalAction) return void 0;
|
|
6533
|
+
if (seenActionIds.has(actionId)) continue;
|
|
6534
|
+
seenActionIds.add(actionId);
|
|
6535
|
+
parts.push({
|
|
6536
|
+
type: "action",
|
|
6537
|
+
action: canonicalAction
|
|
6538
|
+
});
|
|
6539
|
+
}
|
|
6540
|
+
const orderedText = parts.filter(
|
|
6541
|
+
(part) => part.type === "text"
|
|
6542
|
+
).map((part) => part.text).join("");
|
|
6543
|
+
return orderedText === canonicalContent ? parts : void 0;
|
|
6351
6544
|
}
|
|
6352
6545
|
function stringifyTranscriptValue(value, fallback = "") {
|
|
6353
6546
|
if (typeof value === "string") {
|
|
@@ -6367,12 +6560,139 @@ function stringifyTranscriptValue(value, fallback = "") {
|
|
|
6367
6560
|
return String(value);
|
|
6368
6561
|
}
|
|
6369
6562
|
}
|
|
6370
|
-
function
|
|
6563
|
+
function latestInputEditSummary(metadata) {
|
|
6564
|
+
const lastInputEdit = asRecord3(metadata.lastInputEdit);
|
|
6565
|
+
if (!lastInputEdit) return null;
|
|
6566
|
+
const source = asString(lastInputEdit.source) || "unknown";
|
|
6567
|
+
const actor = asString(lastInputEdit.actorSubjectId) || asString(lastInputEdit.actorPermissionProfileName) || asString(lastInputEdit.jobId) || null;
|
|
6568
|
+
const inputKeys = Array.isArray(lastInputEdit.changedInputKeys) ? lastInputEdit.changedInputKeys.filter(
|
|
6569
|
+
(key) => typeof key === "string" && key.trim().length > 0
|
|
6570
|
+
).slice(0, 6) : [];
|
|
6571
|
+
const relationshipKeys = Array.isArray(lastInputEdit.changedRelationshipKeys) ? lastInputEdit.changedRelationshipKeys.filter(
|
|
6572
|
+
(key) => typeof key === "string" && key.trim().length > 0
|
|
6573
|
+
).slice(0, 6) : [];
|
|
6574
|
+
const changed = [
|
|
6575
|
+
inputKeys.length ? `inputs=${inputKeys.join(",")}` : null,
|
|
6576
|
+
relationshipKeys.length ? `relationships=${relationshipKeys.join(",")}` : null
|
|
6577
|
+
].filter(Boolean);
|
|
6578
|
+
return `lastEdit=${source}${actor ? ` by ${actor}` : ""}${changed.length ? ` (${changed.join("; ")})` : ""}`;
|
|
6579
|
+
}
|
|
6580
|
+
function artifactIssueSummary(record) {
|
|
6581
|
+
const validation = asRecord3(record.validation);
|
|
6582
|
+
if (!validation) return null;
|
|
6583
|
+
const issues = Array.isArray(validation.issues) ? validation.issues.map((issue) => asRecord3(issue)).filter((issue) => Boolean(issue)).slice(0, 3) : [];
|
|
6584
|
+
if (issues.length > 0) {
|
|
6585
|
+
return `issues=${issues.map((issue) => {
|
|
6586
|
+
const code = asString(issue.code) || asString(issue.kind) || "issue";
|
|
6587
|
+
const path = asString(issue.path);
|
|
6588
|
+
const message = trimString(issue.message);
|
|
6589
|
+
return `${code}${path ? ` at ${path}` : ""}${message ? ` (${message})` : ""}`;
|
|
6590
|
+
}).join("; ")}`;
|
|
6591
|
+
}
|
|
6592
|
+
const error = trimString(validation.error) || trimString(validation.reason) || trimString(validation.message);
|
|
6593
|
+
return error ? `validation=${error}` : null;
|
|
6594
|
+
}
|
|
6595
|
+
function artifactExecutionSummary(metadata) {
|
|
6596
|
+
const execution = asRecord3(metadata.execution);
|
|
6597
|
+
if (!execution) return null;
|
|
6598
|
+
const result = asRecord3(execution.result);
|
|
6599
|
+
const awaiting = asString(result?.awaiting) || asString(execution.awaiting);
|
|
6600
|
+
const pendingTransition = asString(result?.pendingTransition) || asString(execution.pendingTransition);
|
|
6601
|
+
const approval = asRecord3(result?.approval) || asRecord3(execution.approval);
|
|
6602
|
+
const approvalTarget = asString(approval?.permissionProfileName) || asString(approval?.permissionProfileId) || asString(approval?.assigneeSubjectId);
|
|
6603
|
+
const error = trimString(execution.error);
|
|
6604
|
+
const pieces = [
|
|
6605
|
+
awaiting ? `awaiting=${awaiting}` : null,
|
|
6606
|
+
pendingTransition ? `pendingTransition=${pendingTransition}` : null,
|
|
6607
|
+
approvalTarget ? `approvalTarget=${approvalTarget}` : null,
|
|
6608
|
+
error ? `executionError=${error}` : null
|
|
6609
|
+
].filter(Boolean);
|
|
6610
|
+
return pieces.length ? pieces.join("; ") : null;
|
|
6611
|
+
}
|
|
6612
|
+
function artifactStatePathSummary(metadata) {
|
|
6613
|
+
const statePlan = asRecord3(metadata.statePlan);
|
|
6614
|
+
if (!statePlan) return null;
|
|
6615
|
+
const machineName = asString(statePlan.machineName);
|
|
6616
|
+
const targetState = asString(statePlan.targetState);
|
|
6617
|
+
const objectPath = asString(statePlan.objectPath);
|
|
6618
|
+
const approvedTransitions = Array.isArray(statePlan.approvedTransitions) ? statePlan.approvedTransitions.length : 0;
|
|
6619
|
+
const approvalDecisions = Array.isArray(statePlan.approvalDecisions) ? statePlan.approvalDecisions.length : 0;
|
|
6620
|
+
const pieces = [
|
|
6621
|
+
machineName || targetState ? `statePath=${machineName || "state_machine"}${targetState ? ` -> ${targetState}` : ""}` : null,
|
|
6622
|
+
objectPath ? `objectPath=${objectPath}` : null,
|
|
6623
|
+
approvedTransitions ? `approvedTransitions=${approvedTransitions}` : null,
|
|
6624
|
+
approvalDecisions ? `approvalDecisions=${approvalDecisions}` : null
|
|
6625
|
+
].filter(Boolean);
|
|
6626
|
+
return pieces.length ? pieces.join("; ") : null;
|
|
6627
|
+
}
|
|
6628
|
+
function artifactSummaryLine(artifactId, record) {
|
|
6629
|
+
if (!record) return `- ${artifactId}: unavailable in session artifact store`;
|
|
6630
|
+
const label = trimString(record.label) || artifactId;
|
|
6631
|
+
const kind = asString(record.kind) || "artifact";
|
|
6632
|
+
const status = asString(record.status) || "unknown";
|
|
6633
|
+
const createdByJobId = asString(record.createdByJobId);
|
|
6634
|
+
const target = asRecord3(record.target);
|
|
6635
|
+
const metadata = asRecord3(record.metadata) || {};
|
|
6636
|
+
const subArtifactIds = Array.isArray(record.subArtifactIds) ? record.subArtifactIds.filter(
|
|
6637
|
+
(id) => typeof id === "string" && id.trim().length > 0
|
|
6638
|
+
).slice(0, 8) : [];
|
|
6639
|
+
const relationships = compactJson(record.relationships, 220);
|
|
6640
|
+
const pieces = [
|
|
6641
|
+
`kind=${kind}`,
|
|
6642
|
+
`status=${status}`,
|
|
6643
|
+
createdByJobId ? `createdByJob=${createdByJobId}` : null,
|
|
6644
|
+
target ? `target=${asString(target.className) || "record"}:${asString(target.id) || "unknown"}${asString(target.label) ? ` (${asString(target.label)})` : ""}` : null,
|
|
6645
|
+
artifactStatePathSummary(metadata),
|
|
6646
|
+
artifactExecutionSummary(metadata),
|
|
6647
|
+
artifactIssueSummary(record),
|
|
6648
|
+
latestInputEditSummary(metadata),
|
|
6649
|
+
subArtifactIds.length ? `subArtifacts=${subArtifactIds.join(",")}` : null,
|
|
6650
|
+
relationships ? `relationships=${relationships}` : null
|
|
6651
|
+
].filter(Boolean);
|
|
6652
|
+
return `- ${artifactId}: ${label}${pieces.length ? `; ${pieces.join("; ")}` : ""}`;
|
|
6653
|
+
}
|
|
6654
|
+
function buildArtifactHistory(show, artifactsById) {
|
|
6371
6655
|
if (!show) return void 0;
|
|
6372
|
-
|
|
6656
|
+
const artifactIds = show.sessionArtifactIds || [];
|
|
6657
|
+
const actionSuggestions = show.actionSuggestions || [];
|
|
6658
|
+
if (artifactIds.length === 0 && actionSuggestions.length === 0) {
|
|
6659
|
+
return `[Agent message]
|
|
6373
6660
|
${stringifyTranscriptValue({ show }, "")}`;
|
|
6661
|
+
}
|
|
6662
|
+
const lines = artifactIds.slice(0, 8).map(
|
|
6663
|
+
(artifactId) => artifactSummaryLine(artifactId, artifactsById?.[artifactId])
|
|
6664
|
+
);
|
|
6665
|
+
if (artifactIds.length > 8) {
|
|
6666
|
+
lines.push(`- ${artifactIds.length - 8} more artifacts omitted`);
|
|
6667
|
+
}
|
|
6668
|
+
if (actionSuggestions.length > 0) {
|
|
6669
|
+
if (artifactIds.length > 0) lines.push("[Agent suggested actions]");
|
|
6670
|
+
for (const suggestion of actionSuggestions.slice(0, 8)) {
|
|
6671
|
+
lines.push(
|
|
6672
|
+
`- ${suggestion.label}${suggestion.description ? `; ${suggestion.description}` : ""}`
|
|
6673
|
+
);
|
|
6674
|
+
}
|
|
6675
|
+
if (actionSuggestions.length > 8) {
|
|
6676
|
+
lines.push(`- ${actionSuggestions.length - 8} more suggestions omitted`);
|
|
6677
|
+
}
|
|
6678
|
+
}
|
|
6679
|
+
const otherRefs = {
|
|
6680
|
+
entryPaths: show.entryPaths,
|
|
6681
|
+
listNames: show.listNames,
|
|
6682
|
+
variableNames: show.variableNames,
|
|
6683
|
+
fileIds: show.fileIds
|
|
6684
|
+
};
|
|
6685
|
+
const hasOtherRefs = Object.values(otherRefs).some(
|
|
6686
|
+
(value) => Array.isArray(value) && value.length > 0
|
|
6687
|
+
);
|
|
6688
|
+
const title = artifactIds.length > 0 ? "[Agent displayed session artifacts]" : "[Agent suggested actions]";
|
|
6689
|
+
return [
|
|
6690
|
+
title,
|
|
6691
|
+
...lines,
|
|
6692
|
+
hasOtherRefs ? `Other shown refs: ${stringifyTranscriptValue(otherRefs, "")}` : null
|
|
6693
|
+
].filter(Boolean).join("\n");
|
|
6374
6694
|
}
|
|
6375
|
-
function normalizeConversationMessage(raw) {
|
|
6695
|
+
function normalizeConversationMessage(raw, artifactsById) {
|
|
6376
6696
|
const record = asRecord3(raw);
|
|
6377
6697
|
if (!record) return null;
|
|
6378
6698
|
const role = record.role === "user" ? "user" : record.role === "assistant" ? "assistant" : null;
|
|
@@ -6380,10 +6700,18 @@ function normalizeConversationMessage(raw) {
|
|
|
6380
6700
|
const content = trimString(
|
|
6381
6701
|
record.content ?? record.reply ?? record.message ?? record.text
|
|
6382
6702
|
);
|
|
6703
|
+
const actions = role === "assistant" ? normalizeConversationMessageActions(record.actions) : void 0;
|
|
6704
|
+
const parts = role === "assistant" ? normalizeConversationMessageParts(record.parts, content, actions) : void 0;
|
|
6383
6705
|
const show = normalizeShowRefs(record.show);
|
|
6384
6706
|
const id = asString(record.id) || crypto.randomUUID();
|
|
6385
6707
|
const timestamp = asNumber(record.timestamp) || asNumber(record.ts) || 0;
|
|
6386
|
-
if (!content && !show) return null;
|
|
6708
|
+
if (!content && !show && !actions?.length) return null;
|
|
6709
|
+
const artifactHistory = buildArtifactHistory(show, artifactsById);
|
|
6710
|
+
const historyContent = role === "assistant" ? content && artifactHistory ? `[Assistant reply]
|
|
6711
|
+
${content}
|
|
6712
|
+
|
|
6713
|
+
${artifactHistory}` : content ? `[Assistant reply]
|
|
6714
|
+
${content}` : artifactHistory : void 0;
|
|
6387
6715
|
return {
|
|
6388
6716
|
id,
|
|
6389
6717
|
role,
|
|
@@ -6392,8 +6720,9 @@ function normalizeConversationMessage(raw) {
|
|
|
6392
6720
|
jobId: asString(record.jobId),
|
|
6393
6721
|
promptId: asString(record.promptId),
|
|
6394
6722
|
show,
|
|
6395
|
-
|
|
6396
|
-
|
|
6723
|
+
actions,
|
|
6724
|
+
parts,
|
|
6725
|
+
historyContent,
|
|
6397
6726
|
source: "conversation"
|
|
6398
6727
|
};
|
|
6399
6728
|
}
|
|
@@ -6436,7 +6765,7 @@ ${assistantContent}`,
|
|
|
6436
6765
|
return entries;
|
|
6437
6766
|
});
|
|
6438
6767
|
}
|
|
6439
|
-
function normalizeAgentMessageEntries(jobId, rawMessages) {
|
|
6768
|
+
function normalizeAgentMessageEntries(jobId, rawMessages, artifactsById) {
|
|
6440
6769
|
return asArray(rawMessages).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
|
|
6441
6770
|
(left, right) => (asNumber(left.timestamp) || asNumber(left.ts) || 0) - (asNumber(right.timestamp) || asNumber(right.ts) || 0)
|
|
6442
6771
|
).flatMap((message) => {
|
|
@@ -6467,14 +6796,14 @@ ${reply}`,
|
|
|
6467
6796
|
timestamp,
|
|
6468
6797
|
jobId,
|
|
6469
6798
|
show,
|
|
6470
|
-
historyContent: buildArtifactHistory(show),
|
|
6799
|
+
historyContent: buildArtifactHistory(show, artifactsById),
|
|
6471
6800
|
source: "job_agent_message"
|
|
6472
6801
|
});
|
|
6473
6802
|
}
|
|
6474
6803
|
return entries;
|
|
6475
6804
|
});
|
|
6476
6805
|
}
|
|
6477
|
-
function buildJobFallbackEntries(jobId, job, sessionHeap) {
|
|
6806
|
+
function buildJobFallbackEntries(jobId, job, sessionHeap, artifactsById) {
|
|
6478
6807
|
const timestamp = asNumber(job.finishedAt) || asNumber(job.startedAt) || asNumber(job.submittedAt) || 0;
|
|
6479
6808
|
const resultPreview = stringifyTranscriptValue(
|
|
6480
6809
|
job.result,
|
|
@@ -6512,7 +6841,7 @@ ${responseText}`,
|
|
|
6512
6841
|
timestamp,
|
|
6513
6842
|
jobId,
|
|
6514
6843
|
show,
|
|
6515
|
-
historyContent: buildArtifactHistory(show),
|
|
6844
|
+
historyContent: buildArtifactHistory(show, artifactsById),
|
|
6516
6845
|
source: "job_result"
|
|
6517
6846
|
});
|
|
6518
6847
|
}
|
|
@@ -6566,10 +6895,11 @@ function buildJobCodeEntry(jobId, job) {
|
|
|
6566
6895
|
function buildSessionTranscript(input) {
|
|
6567
6896
|
const liveDoc = input.liveDoc || null;
|
|
6568
6897
|
const sessionHeap = input.sessionHeap || EMPTY_HEAP;
|
|
6898
|
+
const artifactsById = artifactRecordsById(liveDoc);
|
|
6569
6899
|
const transcript = [];
|
|
6570
6900
|
const conversationMessages = asArray(
|
|
6571
6901
|
asRecord3(liveDoc?.conversation)?.messages
|
|
6572
|
-
).map((message) => normalizeConversationMessage(message)).filter((message) => Boolean(message));
|
|
6902
|
+
).map((message) => normalizeConversationMessage(message, artifactsById)).filter((message) => Boolean(message));
|
|
6573
6903
|
const conversationPromptIds = new Set(
|
|
6574
6904
|
conversationMessages.map((message) => message.promptId).filter((promptId) => Boolean(promptId))
|
|
6575
6905
|
);
|
|
@@ -6596,7 +6926,8 @@ function buildSessionTranscript(input) {
|
|
|
6596
6926
|
if (!assistantConversationJobIds.has(jobId)) {
|
|
6597
6927
|
const agentEntries = normalizeAgentMessageEntries(
|
|
6598
6928
|
jobId,
|
|
6599
|
-
job.agentMessages
|
|
6929
|
+
job.agentMessages,
|
|
6930
|
+
artifactsById
|
|
6600
6931
|
);
|
|
6601
6932
|
if (agentEntries.length > 0) {
|
|
6602
6933
|
transcript.push(...agentEntries);
|
|
@@ -6605,7 +6936,8 @@ function buildSessionTranscript(input) {
|
|
|
6605
6936
|
...buildJobFallbackEntries(
|
|
6606
6937
|
jobId,
|
|
6607
6938
|
job,
|
|
6608
|
-
sessionHeap
|
|
6939
|
+
sessionHeap,
|
|
6940
|
+
artifactsById
|
|
6609
6941
|
)
|
|
6610
6942
|
);
|
|
6611
6943
|
}
|
|
@@ -10771,16 +11103,107 @@ var StateMachineStateSchema = external_exports.union([
|
|
|
10771
11103
|
external_exports.string(),
|
|
10772
11104
|
external_exports.object({
|
|
10773
11105
|
name: external_exports.string().min(1),
|
|
11106
|
+
label: external_exports.string().optional(),
|
|
11107
|
+
description: external_exports.string().optional(),
|
|
10774
11108
|
isFinal: external_exports.boolean().optional()
|
|
10775
11109
|
}).strict()
|
|
10776
11110
|
]);
|
|
11111
|
+
var StateTransitionInputBindingSchema = external_exports.lazy(
|
|
11112
|
+
() => external_exports.union([
|
|
11113
|
+
external_exports.null(),
|
|
11114
|
+
external_exports.string(),
|
|
11115
|
+
external_exports.number(),
|
|
11116
|
+
external_exports.boolean(),
|
|
11117
|
+
external_exports.array(StateTransitionInputBindingSchema),
|
|
11118
|
+
external_exports.object({
|
|
11119
|
+
const: external_exports.unknown()
|
|
11120
|
+
}).strict(),
|
|
11121
|
+
external_exports.object({
|
|
11122
|
+
from: external_exports.literal("object"),
|
|
11123
|
+
path: external_exports.string().min(1),
|
|
11124
|
+
editable: external_exports.boolean().optional()
|
|
11125
|
+
}).strict(),
|
|
11126
|
+
external_exports.object({
|
|
11127
|
+
from: external_exports.literal("field"),
|
|
11128
|
+
name: external_exports.string().min(1),
|
|
11129
|
+
editable: external_exports.boolean().optional()
|
|
11130
|
+
}).strict(),
|
|
11131
|
+
external_exports.object({
|
|
11132
|
+
from: external_exports.literal("relationship"),
|
|
11133
|
+
name: external_exports.string().min(1),
|
|
11134
|
+
path: external_exports.string().min(1).optional(),
|
|
11135
|
+
many: external_exports.boolean().optional(),
|
|
11136
|
+
editable: external_exports.boolean().optional()
|
|
11137
|
+
}).strict(),
|
|
11138
|
+
external_exports.object({
|
|
11139
|
+
from: external_exports.literal("session"),
|
|
11140
|
+
path: external_exports.string().min(1),
|
|
11141
|
+
editable: external_exports.boolean().optional()
|
|
11142
|
+
}).strict(),
|
|
11143
|
+
external_exports.object({
|
|
11144
|
+
from: external_exports.literal("actor"),
|
|
11145
|
+
path: external_exports.string().min(1),
|
|
11146
|
+
editable: external_exports.boolean().optional()
|
|
11147
|
+
}).strict(),
|
|
11148
|
+
external_exports.record(external_exports.string(), StateTransitionInputBindingSchema)
|
|
11149
|
+
])
|
|
11150
|
+
);
|
|
11151
|
+
var StateTransitionActionSchema = external_exports.object({
|
|
11152
|
+
effect: external_exports.string().min(1),
|
|
11153
|
+
input: external_exports.record(external_exports.string(), StateTransitionInputBindingSchema).optional()
|
|
11154
|
+
}).strict();
|
|
11155
|
+
var StateTransitionAssigneeSchema = external_exports.object({
|
|
11156
|
+
kind: external_exports.string().min(1),
|
|
11157
|
+
from: StateTransitionInputBindingSchema.optional(),
|
|
11158
|
+
role: external_exports.string().optional(),
|
|
11159
|
+
label: external_exports.string().optional()
|
|
11160
|
+
}).strict();
|
|
11161
|
+
var StateTransitionRelatedStateRequirementSchema = external_exports.object({
|
|
11162
|
+
relationship: external_exports.string().min(1),
|
|
11163
|
+
machine: external_exports.string().min(1),
|
|
11164
|
+
state: external_exports.string().min(1),
|
|
11165
|
+
className: external_exports.string().min(1).optional(),
|
|
11166
|
+
label: external_exports.string().optional(),
|
|
11167
|
+
mode: external_exports.enum(["every", "some", "any"]).optional()
|
|
11168
|
+
}).strict();
|
|
11169
|
+
var StateTransitionRequirementsSchema = external_exports.object({
|
|
11170
|
+
fields: external_exports.array(external_exports.string().min(1)).optional(),
|
|
11171
|
+
relationships: external_exports.array(external_exports.string().min(1)).optional(),
|
|
11172
|
+
relatedStates: external_exports.array(StateTransitionRelatedStateRequirementSchema).optional()
|
|
11173
|
+
}).strict();
|
|
11174
|
+
var StateTransitionPermissionSchema = external_exports.union([
|
|
11175
|
+
external_exports.string().min(1),
|
|
11176
|
+
external_exports.object({
|
|
11177
|
+
profile: external_exports.string().min(1).optional(),
|
|
11178
|
+
profileId: external_exports.string().min(1).optional(),
|
|
11179
|
+
label: external_exports.string().optional(),
|
|
11180
|
+
reason: external_exports.string().optional()
|
|
11181
|
+
}).strict()
|
|
11182
|
+
]);
|
|
11183
|
+
var StateTransitionExpectedOutcomeSchema = external_exports.union([
|
|
11184
|
+
external_exports.string().min(1),
|
|
11185
|
+
external_exports.object({
|
|
11186
|
+
machine: external_exports.string().min(1).optional(),
|
|
11187
|
+
state: external_exports.string().min(1),
|
|
11188
|
+
summary: external_exports.string().optional()
|
|
11189
|
+
}).strict()
|
|
11190
|
+
]);
|
|
10777
11191
|
var StateMachineTransitionSchema = external_exports.object({
|
|
10778
11192
|
name: external_exports.string().min(1),
|
|
10779
11193
|
from: external_exports.string().min(1),
|
|
10780
|
-
to: external_exports.string().min(1)
|
|
11194
|
+
to: external_exports.string().min(1),
|
|
11195
|
+
label: external_exports.string().optional(),
|
|
11196
|
+
description: external_exports.string().optional(),
|
|
11197
|
+
action: StateTransitionActionSchema.optional(),
|
|
11198
|
+
assignee: StateTransitionAssigneeSchema.optional(),
|
|
11199
|
+
requirements: StateTransitionRequirementsSchema.optional(),
|
|
11200
|
+
permission: StateTransitionPermissionSchema.optional(),
|
|
11201
|
+
risk: external_exports.enum(["low", "medium", "high"]).optional(),
|
|
11202
|
+
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional()
|
|
10781
11203
|
}).strict();
|
|
10782
11204
|
external_exports.object({
|
|
10783
11205
|
name: external_exports.string().min(1),
|
|
11206
|
+
stateField: external_exports.string().min(1).optional(),
|
|
10784
11207
|
entryState: external_exports.string().min(1),
|
|
10785
11208
|
states: external_exports.array(StateMachineStateSchema).min(1),
|
|
10786
11209
|
transitions: external_exports.array(StateMachineTransitionSchema),
|
|
@@ -10847,6 +11270,16 @@ var PoliciesSchema = external_exports.object({
|
|
|
10847
11270
|
confirmWhen: external_exports.array(PolicyRuleSchema).optional(),
|
|
10848
11271
|
denyWhen: external_exports.array(PolicyRuleSchema).optional()
|
|
10849
11272
|
}).strict();
|
|
11273
|
+
var CreatesSchema = external_exports.union([
|
|
11274
|
+
external_exports.string().min(1),
|
|
11275
|
+
external_exports.object({
|
|
11276
|
+
className: external_exports.string().min(1),
|
|
11277
|
+
idPath: external_exports.string().min(1).optional(),
|
|
11278
|
+
pathPath: external_exports.string().min(1).optional(),
|
|
11279
|
+
statePath: external_exports.string().min(1).optional(),
|
|
11280
|
+
classStateHandle: external_exports.boolean().optional()
|
|
11281
|
+
}).strict()
|
|
11282
|
+
]);
|
|
10850
11283
|
external_exports.object({
|
|
10851
11284
|
postCondition: external_exports.union([
|
|
10852
11285
|
external_exports.string(),
|
|
@@ -10877,6 +11310,7 @@ external_exports.object({
|
|
|
10877
11310
|
mode: external_exports.string().optional()
|
|
10878
11311
|
}).strict()
|
|
10879
11312
|
]).optional(),
|
|
11313
|
+
creates: CreatesSchema.optional(),
|
|
10880
11314
|
access: external_exports.enum(["read", "write", "ui"]).optional(),
|
|
10881
11315
|
effectKind: external_exports.enum(["read", "write", "ui"]).optional(),
|
|
10882
11316
|
sideEffect: external_exports.enum(["read", "write", "ui", "readonly", "read_only"]).optional(),
|
|
@@ -11104,9 +11538,10 @@ function mergeMethodSummaryPatch(target, patch) {
|
|
|
11104
11538
|
if (patch.metamodels !== void 0) target.metamodels = patch.metamodels;
|
|
11105
11539
|
if (patch.effectBehaviors !== void 0)
|
|
11106
11540
|
target.effectBehaviors = patch.effectBehaviors;
|
|
11541
|
+
if (patch.creates !== void 0) target.creates = patch.creates;
|
|
11107
11542
|
if (patch.static !== void 0) target.static = patch.static;
|
|
11108
11543
|
}
|
|
11109
|
-
function
|
|
11544
|
+
function toPascalCase2(value) {
|
|
11110
11545
|
return value.split(/[_:\-\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
11111
11546
|
}
|
|
11112
11547
|
function normalizeNotesInput(input) {
|
|
@@ -11164,29 +11599,60 @@ function normalizeEffectBehaviorSummary(metamodels) {
|
|
|
11164
11599
|
}
|
|
11165
11600
|
return Object.keys(result).length > 0 ? result : null;
|
|
11166
11601
|
}
|
|
11167
|
-
function
|
|
11168
|
-
if (!
|
|
11169
|
-
|
|
11602
|
+
function normalizeCreationSummary(metamodels) {
|
|
11603
|
+
if (!isObject(metamodels)) return null;
|
|
11604
|
+
let raw = metamodels.creates;
|
|
11605
|
+
if (typeof raw === "string" && raw.trim().length > 0) {
|
|
11606
|
+
const trimmed = raw.trim();
|
|
11607
|
+
if (trimmed.startsWith("{") || trimmed.startsWith('"')) {
|
|
11608
|
+
try {
|
|
11609
|
+
raw = JSON.parse(trimmed);
|
|
11610
|
+
} catch {
|
|
11611
|
+
return { className: trimmed };
|
|
11612
|
+
}
|
|
11613
|
+
} else {
|
|
11614
|
+
return { className: trimmed };
|
|
11615
|
+
}
|
|
11616
|
+
}
|
|
11617
|
+
if (typeof raw === "string" && raw.trim().length > 0) {
|
|
11618
|
+
return { className: raw.trim() };
|
|
11170
11619
|
}
|
|
11620
|
+
if (!isObject(raw)) return null;
|
|
11621
|
+
const className = typeof raw.className === "string" && raw.className.trim() ? raw.className.trim() : "";
|
|
11622
|
+
if (!className) return null;
|
|
11623
|
+
return {
|
|
11624
|
+
className,
|
|
11625
|
+
...typeof raw.idPath === "string" && raw.idPath.trim() ? { idPath: raw.idPath.trim() } : {},
|
|
11626
|
+
...typeof raw.pathPath === "string" && raw.pathPath.trim() ? { pathPath: raw.pathPath.trim() } : {},
|
|
11627
|
+
...typeof raw.statePath === "string" && raw.statePath.trim() ? { statePath: raw.statePath.trim() } : {},
|
|
11628
|
+
...typeof raw.classStateHandle === "boolean" ? { classStateHandle: raw.classStateHandle } : {}
|
|
11629
|
+
};
|
|
11630
|
+
}
|
|
11631
|
+
function buildEffectBehaviorDocs(effectBehaviors, creates) {
|
|
11171
11632
|
const docs = [];
|
|
11172
|
-
if (
|
|
11633
|
+
if (creates) {
|
|
11634
|
+
docs.push(
|
|
11635
|
+
`Creation method: creates ${creates.className}. The agent may use generated class-level new-record action methods for this class.`
|
|
11636
|
+
);
|
|
11637
|
+
}
|
|
11638
|
+
if (effectBehaviors?.approvalRequired?.required) {
|
|
11173
11639
|
docs.push(
|
|
11174
11640
|
effectBehaviors.approvalRequired.reason ? `Approval required: ${effectBehaviors.approvalRequired.reason}.` : "Approval required before execution."
|
|
11175
11641
|
);
|
|
11176
11642
|
}
|
|
11177
|
-
if (effectBehaviors
|
|
11643
|
+
if (effectBehaviors?.postCondition) {
|
|
11178
11644
|
docs.push(`Post-condition: ${effectBehaviors.postCondition.condition}.`);
|
|
11179
11645
|
if (effectBehaviors.postCondition.description) {
|
|
11180
11646
|
docs.push(effectBehaviors.postCondition.description);
|
|
11181
11647
|
}
|
|
11182
11648
|
}
|
|
11183
|
-
if (effectBehaviors
|
|
11649
|
+
if (effectBehaviors?.dryRun?.enabled) {
|
|
11184
11650
|
docs.push("Supports dry run.");
|
|
11185
11651
|
if (effectBehaviors.dryRun.description) {
|
|
11186
11652
|
docs.push(effectBehaviors.dryRun.description);
|
|
11187
11653
|
}
|
|
11188
11654
|
}
|
|
11189
|
-
if (effectBehaviors
|
|
11655
|
+
if (effectBehaviors?.reverse) {
|
|
11190
11656
|
if (effectBehaviors.reverse.handler) {
|
|
11191
11657
|
docs.push(`Reverse handler: ${effectBehaviors.reverse.handler}.`);
|
|
11192
11658
|
} else {
|
|
@@ -11245,13 +11711,21 @@ function buildEffectBehaviorMutations(toolPath, spec) {
|
|
|
11245
11711
|
query: `mutation { at(path: ${JSON.stringify(toolPath)}) { set_approval_required(${args}) { kind } } }`
|
|
11246
11712
|
});
|
|
11247
11713
|
}
|
|
11714
|
+
if (spec.creates !== void 0) {
|
|
11715
|
+
mutations.push({
|
|
11716
|
+
label: `set creates on ${toolPath}`,
|
|
11717
|
+
query: `mutation { at(path: ${JSON.stringify(toolPath)}) { create_submodel(subpath: "creates", label: "creates") { set_string_value(value: ${JSON.stringify(
|
|
11718
|
+
JSON.stringify(spec.creates)
|
|
11719
|
+
)}) { done } } } }`
|
|
11720
|
+
});
|
|
11721
|
+
}
|
|
11248
11722
|
return mutations;
|
|
11249
11723
|
}
|
|
11250
11724
|
function readMethodEffectBehaviors(rawMethod) {
|
|
11725
|
+
const metamodels = isObject(rawMethod.metamodels) ? rawMethod.metamodels : null;
|
|
11251
11726
|
return {
|
|
11252
|
-
effectBehaviors: normalizeEffectBehaviorSummary(
|
|
11253
|
-
|
|
11254
|
-
)
|
|
11727
|
+
effectBehaviors: normalizeEffectBehaviorSummary(metamodels),
|
|
11728
|
+
creates: normalizeCreationSummary(metamodels)
|
|
11255
11729
|
};
|
|
11256
11730
|
}
|
|
11257
11731
|
var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
@@ -11273,6 +11747,10 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
11273
11747
|
{
|
|
11274
11748
|
key: "approvalRequired",
|
|
11275
11749
|
description: "Boolean or `{ required, reason, mode }`."
|
|
11750
|
+
},
|
|
11751
|
+
{
|
|
11752
|
+
key: "creates",
|
|
11753
|
+
description: 'Marks a static method as an allowed creator for a class. Use `creates: "class_name"` or `{ className, idPath, pathPath, statePath, classStateHandle }`.'
|
|
11276
11754
|
}
|
|
11277
11755
|
]
|
|
11278
11756
|
},
|
|
@@ -11392,7 +11870,10 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
11392
11870
|
...methodIR,
|
|
11393
11871
|
docs: [
|
|
11394
11872
|
...methodIR.docs,
|
|
11395
|
-
...buildEffectBehaviorDocs(
|
|
11873
|
+
...buildEffectBehaviorDocs(
|
|
11874
|
+
methodSummary.effectBehaviors,
|
|
11875
|
+
methodSummary.creates
|
|
11876
|
+
)
|
|
11396
11877
|
]
|
|
11397
11878
|
};
|
|
11398
11879
|
}
|
|
@@ -11633,15 +12114,50 @@ function toRecordSearchResult(className, node) {
|
|
|
11633
12114
|
return [];
|
|
11634
12115
|
}
|
|
11635
12116
|
) : [];
|
|
12117
|
+
const graphPathId = extractRecordIdFromGraphPath(path, className);
|
|
12118
|
+
const realIdField = fields.find(
|
|
12119
|
+
(field) => normalizeGraphPathSegment(field.name) === "real_id" && typeof field.value === "string" && field.value.trim()
|
|
12120
|
+
);
|
|
12121
|
+
const id = typeof realIdField?.value === "string" ? realIdField.value.trim() : graphPathId;
|
|
12122
|
+
const rawLabel = typeof node.label === "string" && node.label.trim() ? node.label : "";
|
|
12123
|
+
if (fields.length === 0 && rawLabel && isPlaceholderRecordLabel(rawLabel, graphPathId, path)) {
|
|
12124
|
+
return null;
|
|
12125
|
+
}
|
|
12126
|
+
const fallbackLabel = displayLabelFromFields(fields);
|
|
12127
|
+
const label = rawLabel && !isPlaceholderRecordLabel(rawLabel, id, path) ? rawLabel : fallbackLabel || rawLabel || id;
|
|
11636
12128
|
return {
|
|
11637
12129
|
path,
|
|
11638
12130
|
className,
|
|
11639
|
-
id
|
|
11640
|
-
label
|
|
12131
|
+
id,
|
|
12132
|
+
label,
|
|
11641
12133
|
description: typeof node.description === "string" && node.description.trim() ? node.description : null,
|
|
11642
12134
|
fields
|
|
11643
12135
|
};
|
|
11644
12136
|
}
|
|
12137
|
+
function isPlaceholderRecordLabel(label, id, path) {
|
|
12138
|
+
const normalizedLabel = normalizeGraphPathSegment(label);
|
|
12139
|
+
return normalizedLabel === normalizeGraphPathSegment(id) || normalizedLabel === normalizeGraphPathSegment(path);
|
|
12140
|
+
}
|
|
12141
|
+
function displayLabelFromFields(fields) {
|
|
12142
|
+
const preferredFieldNames = [
|
|
12143
|
+
"name",
|
|
12144
|
+
"title",
|
|
12145
|
+
"label",
|
|
12146
|
+
"display_name",
|
|
12147
|
+
"file_name",
|
|
12148
|
+
"number",
|
|
12149
|
+
"code"
|
|
12150
|
+
];
|
|
12151
|
+
for (const preferred of preferredFieldNames) {
|
|
12152
|
+
const match = fields.find(
|
|
12153
|
+
(field) => normalizeGraphPathSegment(field.name) === preferred && typeof field.value === "string" && field.value.trim()
|
|
12154
|
+
);
|
|
12155
|
+
if (typeof match?.value === "string") {
|
|
12156
|
+
return match.value.trim();
|
|
12157
|
+
}
|
|
12158
|
+
}
|
|
12159
|
+
return null;
|
|
12160
|
+
}
|
|
11645
12161
|
function normalizeRecordSearchText(value) {
|
|
11646
12162
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
|
|
11647
12163
|
}
|
|
@@ -11783,7 +12299,12 @@ async function recordOpenAIUsageSpend(options) {
|
|
|
11783
12299
|
const metadata = {
|
|
11784
12300
|
...options.metadata || {},
|
|
11785
12301
|
...options.usage.rawUsage !== void 0 ? { openaiUsage: options.usage.rawUsage } : {},
|
|
11786
|
-
usageContext: context
|
|
12302
|
+
usageContext: context,
|
|
12303
|
+
pricingContextTier: options.usage.pricingContextTier,
|
|
12304
|
+
cacheWritePricePerMillionMicros: options.usage.cacheWritePricePerMillionMicros,
|
|
12305
|
+
cacheWriteTokens: options.usage.cacheWriteTokens,
|
|
12306
|
+
cacheWriteCostMicros: options.usage.cacheWriteCostMicros,
|
|
12307
|
+
longContextThresholdTokens: options.usage.longContextThresholdTokens
|
|
11787
12308
|
};
|
|
11788
12309
|
const response = await fetch(
|
|
11789
12310
|
`${toGranularHttpBase(options.apiUrl)}/control/spend/events`,
|
|
@@ -12541,15 +13062,47 @@ var searchableMetamodelPackage = defineMetamodelPackage({
|
|
|
12541
13062
|
|
|
12542
13063
|
// ../metamodel-state-machine/src/index.ts
|
|
12543
13064
|
function normalizeStateMachines(values) {
|
|
13065
|
+
const parseJsonRecord = (value) => {
|
|
13066
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
13067
|
+
return value;
|
|
13068
|
+
}
|
|
13069
|
+
if (typeof value !== "string" || !value.trim()) return null;
|
|
13070
|
+
try {
|
|
13071
|
+
const parsed = JSON.parse(value);
|
|
13072
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
13073
|
+
} catch {
|
|
13074
|
+
return null;
|
|
13075
|
+
}
|
|
13076
|
+
};
|
|
13077
|
+
const parseJsonValue = (value) => {
|
|
13078
|
+
if (value === null || typeof value === "undefined") return null;
|
|
13079
|
+
if (typeof value !== "string") return value;
|
|
13080
|
+
if (!value.trim()) return null;
|
|
13081
|
+
try {
|
|
13082
|
+
return JSON.parse(value);
|
|
13083
|
+
} catch {
|
|
13084
|
+
return value;
|
|
13085
|
+
}
|
|
13086
|
+
};
|
|
12544
13087
|
return (values || []).map((machine) => {
|
|
12545
13088
|
const states = (machine?.states || []).map((state) => ({
|
|
12546
13089
|
name: String(state?.name || ""),
|
|
12547
|
-
|
|
13090
|
+
label: typeof state?.label === "string" ? state.label : null,
|
|
13091
|
+
description: typeof state?.description === "string" ? state.description : null,
|
|
13092
|
+
isFinal: Boolean(state?.is_final ?? state?.isFinal)
|
|
12548
13093
|
})).filter((state) => state.name.length > 0);
|
|
12549
13094
|
const transitions = (machine?.transitions || []).map((transition) => ({
|
|
12550
13095
|
name: String(transition?.name || ""),
|
|
12551
13096
|
from: String(transition?.from?.name || ""),
|
|
12552
|
-
to: String(transition?.to?.name || "")
|
|
13097
|
+
to: String(transition?.to?.name || ""),
|
|
13098
|
+
label: typeof transition?.label === "string" ? transition.label : null,
|
|
13099
|
+
description: typeof transition?.description === "string" ? transition.description : null,
|
|
13100
|
+
action: parseJsonRecord(transition?.action) || parseJsonRecord(transition?.action_json),
|
|
13101
|
+
assignee: parseJsonRecord(transition?.assignee) || parseJsonRecord(transition?.assignee_json),
|
|
13102
|
+
requirements: parseJsonRecord(transition?.requirements) || parseJsonRecord(transition?.requirements_json),
|
|
13103
|
+
permission: parseJsonValue(transition?.permission) ?? parseJsonValue(transition?.permission_json),
|
|
13104
|
+
risk: transition?.risk === "low" || transition?.risk === "medium" || transition?.risk === "high" ? transition.risk : null,
|
|
13105
|
+
expectedOutcome: parseJsonValue(transition?.expectedOutcome) ?? parseJsonValue(transition?.expected_outcome_json)
|
|
12553
13106
|
})).filter(
|
|
12554
13107
|
(transition) => transition.name.length > 0 && transition.from.length > 0 && transition.to.length > 0
|
|
12555
13108
|
);
|
|
@@ -12563,7 +13116,7 @@ function normalizeStateMachines(values) {
|
|
|
12563
13116
|
}).filter((machine) => machine.name.length > 0);
|
|
12564
13117
|
}
|
|
12565
13118
|
function stateTypeName(className, machineName) {
|
|
12566
|
-
return `${
|
|
13119
|
+
return `${toPascalCase2(className)}${toPascalCase2(machineName)}`;
|
|
12567
13120
|
}
|
|
12568
13121
|
function transitionTypeName(className, machineName) {
|
|
12569
13122
|
return `${stateTypeName(className, machineName)}Transition`;
|
|
@@ -12571,6 +13124,15 @@ function transitionTypeName(className, machineName) {
|
|
|
12571
13124
|
function pathTypeName(className, machineName) {
|
|
12572
13125
|
return `${stateTypeName(className, machineName)}Path`;
|
|
12573
13126
|
}
|
|
13127
|
+
function methodToken(value) {
|
|
13128
|
+
const token = String(value || "").trim().replace(/[^A-Za-z0-9_]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
13129
|
+
return token || "state";
|
|
13130
|
+
}
|
|
13131
|
+
function transitionActionsForMachine(machine) {
|
|
13132
|
+
return Object.fromEntries(
|
|
13133
|
+
(machine.transitions || []).filter((transition) => transition.action?.effect).map((transition) => [transition.name, transition.action])
|
|
13134
|
+
);
|
|
13135
|
+
}
|
|
12574
13136
|
function normalizeStateDefinitions(machine) {
|
|
12575
13137
|
const finalStates = new Set(machine.finalStates || []);
|
|
12576
13138
|
const states = /* @__PURE__ */ new Map();
|
|
@@ -12584,6 +13146,8 @@ function normalizeStateDefinitions(machine) {
|
|
|
12584
13146
|
}
|
|
12585
13147
|
states.set(rawState.name, {
|
|
12586
13148
|
name: rawState.name,
|
|
13149
|
+
label: rawState.label,
|
|
13150
|
+
description: rawState.description,
|
|
12587
13151
|
isFinal: Boolean(rawState.isFinal) || finalStates.has(rawState.name)
|
|
12588
13152
|
});
|
|
12589
13153
|
}
|
|
@@ -12595,6 +13159,44 @@ function normalizeStateDefinitions(machine) {
|
|
|
12595
13159
|
}
|
|
12596
13160
|
return [...states.values()];
|
|
12597
13161
|
}
|
|
13162
|
+
function transitionMetadataGraphqlArgs(transition) {
|
|
13163
|
+
const args = [];
|
|
13164
|
+
if (typeof transition.label === "string") {
|
|
13165
|
+
args.push(`label: ${JSON.stringify(transition.label)}`);
|
|
13166
|
+
}
|
|
13167
|
+
if (typeof transition.description === "string") {
|
|
13168
|
+
args.push(`description: ${JSON.stringify(transition.description)}`);
|
|
13169
|
+
}
|
|
13170
|
+
if (transition.action) {
|
|
13171
|
+
args.push(
|
|
13172
|
+
`action_json: ${JSON.stringify(JSON.stringify(transition.action))}`
|
|
13173
|
+
);
|
|
13174
|
+
}
|
|
13175
|
+
if (transition.assignee) {
|
|
13176
|
+
args.push(
|
|
13177
|
+
`assignee_json: ${JSON.stringify(JSON.stringify(transition.assignee))}`
|
|
13178
|
+
);
|
|
13179
|
+
}
|
|
13180
|
+
if (transition.requirements) {
|
|
13181
|
+
args.push(
|
|
13182
|
+
`requirements_json: ${JSON.stringify(JSON.stringify(transition.requirements))}`
|
|
13183
|
+
);
|
|
13184
|
+
}
|
|
13185
|
+
if (transition.permission) {
|
|
13186
|
+
args.push(
|
|
13187
|
+
`permission_json: ${JSON.stringify(JSON.stringify(transition.permission))}`
|
|
13188
|
+
);
|
|
13189
|
+
}
|
|
13190
|
+
if (transition.risk) {
|
|
13191
|
+
args.push(`risk: ${JSON.stringify(transition.risk)}`);
|
|
13192
|
+
}
|
|
13193
|
+
if (transition.expectedOutcome) {
|
|
13194
|
+
args.push(
|
|
13195
|
+
`expected_outcome_json: ${JSON.stringify(JSON.stringify(transition.expectedOutcome))}`
|
|
13196
|
+
);
|
|
13197
|
+
}
|
|
13198
|
+
return args.length > 0 ? `, ${args.join(", ")}` : "";
|
|
13199
|
+
}
|
|
12598
13200
|
function buildStateMachineModelMutations(modelPath, machines) {
|
|
12599
13201
|
const mutations = [];
|
|
12600
13202
|
for (const machine of machines || []) {
|
|
@@ -12605,12 +13207,13 @@ function buildStateMachineModelMutations(modelPath, machines) {
|
|
|
12605
13207
|
)}, entry_state: ${JSON.stringify(machine.entryState)}) { name } } }`
|
|
12606
13208
|
});
|
|
12607
13209
|
for (const state of normalizeStateDefinitions(machine)) {
|
|
12608
|
-
if (state.name === machine.entryState && !state.isFinal)
|
|
13210
|
+
if (state.name === machine.entryState && !state.isFinal && !state.label && !state.description)
|
|
13211
|
+
continue;
|
|
12609
13212
|
mutations.push({
|
|
12610
13213
|
label: `add state ${state.name} on ${modelPath}.${machine.name}`,
|
|
12611
13214
|
query: `mutation { at(path: ${JSON.stringify(modelPath)}) { state_machine(name: ${JSON.stringify(
|
|
12612
13215
|
machine.name
|
|
12613
|
-
)}) { add_state(name: ${JSON.stringify(state.name)}, is_final: ${state.isFinal}) { name } } } }`
|
|
13216
|
+
)}) { add_state(name: ${JSON.stringify(state.name)}, is_final: ${state.isFinal}, label: ${JSON.stringify(state.label || null)}, description: ${JSON.stringify(state.description || null)}) { name } } } }`
|
|
12614
13217
|
});
|
|
12615
13218
|
}
|
|
12616
13219
|
for (const transition of machine.transitions || []) {
|
|
@@ -12622,18 +13225,27 @@ function buildStateMachineModelMutations(modelPath, machines) {
|
|
|
12622
13225
|
transition.name
|
|
12623
13226
|
)}, from: ${JSON.stringify(transition.from)}, to: ${JSON.stringify(
|
|
12624
13227
|
transition.to
|
|
12625
|
-
)}) { name } } } }`
|
|
13228
|
+
)}${transitionMetadataGraphqlArgs(transition)}) { name } } } }`
|
|
12626
13229
|
});
|
|
12627
13230
|
}
|
|
12628
13231
|
}
|
|
12629
13232
|
return mutations;
|
|
12630
13233
|
}
|
|
12631
13234
|
function buildMachineTypes(classSummary, machine) {
|
|
13235
|
+
const stateGlossary = machine.states.map((state) => {
|
|
13236
|
+
const label = state.label && state.label !== state.name ? state.label : null;
|
|
13237
|
+
const meaning = [label, state.description].filter(Boolean).join(" \u2014 ");
|
|
13238
|
+
const finalMarker = state.isFinal ? " Final state." : "";
|
|
13239
|
+
return `${state.name}${meaning ? `: ${meaning}` : "."}${finalMarker}`;
|
|
13240
|
+
});
|
|
12632
13241
|
return [
|
|
12633
13242
|
{
|
|
12634
13243
|
kind: "union",
|
|
12635
13244
|
name: stateTypeName(classSummary.name, machine.name),
|
|
12636
|
-
docs: [
|
|
13245
|
+
docs: [
|
|
13246
|
+
`Allowed states for ${classSummary.name}.${machine.name}.`,
|
|
13247
|
+
...stateGlossary
|
|
13248
|
+
],
|
|
12637
13249
|
members: machine.states.map((state) => state.name)
|
|
12638
13250
|
},
|
|
12639
13251
|
{
|
|
@@ -12649,7 +13261,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
12649
13261
|
const transitionName = transitionTypeName(classSummary.name, machine.name);
|
|
12650
13262
|
pathTypeName(classSummary.name, machine.name);
|
|
12651
13263
|
const docsPrefix = `${classSummary.name}.${machine.name}`;
|
|
12652
|
-
|
|
13264
|
+
const methods = [
|
|
12653
13265
|
{
|
|
12654
13266
|
name: `get_${machine.name}`,
|
|
12655
13267
|
docs: [`Get the current ${docsPrefix} state.`],
|
|
@@ -12672,7 +13284,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
12672
13284
|
],
|
|
12673
13285
|
static: false,
|
|
12674
13286
|
params: [{ name: "target", type: stateName }],
|
|
12675
|
-
returnType: `Promise<${
|
|
13287
|
+
returnType: `Promise<${toPascalCase2(classSummary.name)}>`,
|
|
12676
13288
|
runtime: {
|
|
12677
13289
|
kind: "state_machine",
|
|
12678
13290
|
machineName: machine.name,
|
|
@@ -12745,6 +13357,99 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
12745
13357
|
}
|
|
12746
13358
|
}
|
|
12747
13359
|
];
|
|
13360
|
+
const creationMethods = (classSummary.methods || []).filter(
|
|
13361
|
+
(method) => method.static === true && Boolean(method.creates) && method.creates?.className === classSummary.name && typeof method.effectKey === "string" && method.effectKey.length > 0
|
|
13362
|
+
);
|
|
13363
|
+
for (const state of machine.states) {
|
|
13364
|
+
const stateNameValue = typeof state === "string" ? state : String(state?.name || "");
|
|
13365
|
+
if (!stateNameValue) continue;
|
|
13366
|
+
const token = methodToken(stateNameValue);
|
|
13367
|
+
methods.push(
|
|
13368
|
+
{
|
|
13369
|
+
name: `reach_${machine.name}_to_${token}`,
|
|
13370
|
+
docs: [`Reach ${docsPrefix} state ${stateNameValue}.`],
|
|
13371
|
+
static: false,
|
|
13372
|
+
params: [],
|
|
13373
|
+
returnType: `Promise<${toPascalCase2(classSummary.name)}>`,
|
|
13374
|
+
runtime: {
|
|
13375
|
+
kind: "state_machine",
|
|
13376
|
+
machineName: machine.name,
|
|
13377
|
+
className: classSummary.name,
|
|
13378
|
+
stateTypeName: stateName,
|
|
13379
|
+
transitionTypeName: transitionName,
|
|
13380
|
+
operation: "reach",
|
|
13381
|
+
targetState: stateNameValue,
|
|
13382
|
+
transitionActions: transitionActionsForMachine(machine)
|
|
13383
|
+
}
|
|
13384
|
+
},
|
|
13385
|
+
{
|
|
13386
|
+
name: `prepare_${machine.name}_to_${token}`,
|
|
13387
|
+
docs: [
|
|
13388
|
+
`Prepare a reviewable artifact that can move ${docsPrefix} to ${stateNameValue}.`
|
|
13389
|
+
],
|
|
13390
|
+
static: false,
|
|
13391
|
+
params: [],
|
|
13392
|
+
returnType: "Promise<SessionArtifactRecord>",
|
|
13393
|
+
runtime: {
|
|
13394
|
+
kind: "state_machine",
|
|
13395
|
+
machineName: machine.name,
|
|
13396
|
+
className: classSummary.name,
|
|
13397
|
+
stateTypeName: stateName,
|
|
13398
|
+
transitionTypeName: transitionName,
|
|
13399
|
+
operation: "prepare_reach",
|
|
13400
|
+
targetState: stateNameValue,
|
|
13401
|
+
transitionActions: transitionActionsForMachine(machine)
|
|
13402
|
+
}
|
|
13403
|
+
}
|
|
13404
|
+
);
|
|
13405
|
+
for (const creationMethod of creationMethods) {
|
|
13406
|
+
const creationRuntime = {
|
|
13407
|
+
kind: "state_machine",
|
|
13408
|
+
machineName: machine.name,
|
|
13409
|
+
className: classSummary.name,
|
|
13410
|
+
stateTypeName: stateName,
|
|
13411
|
+
transitionTypeName: transitionName,
|
|
13412
|
+
operation: "prepare_create_reach",
|
|
13413
|
+
targetState: stateNameValue,
|
|
13414
|
+
transitionActions: transitionActionsForMachine(machine),
|
|
13415
|
+
creation: {
|
|
13416
|
+
methodName: creationMethod.name,
|
|
13417
|
+
effectKey: creationMethod.effectKey || creationMethod.name,
|
|
13418
|
+
inputSchema: creationMethod.inputSchema,
|
|
13419
|
+
outputSchema: creationMethod.outputSchema,
|
|
13420
|
+
creates: creationMethod.creates
|
|
13421
|
+
}
|
|
13422
|
+
};
|
|
13423
|
+
const viaName = `prepare_${machine.name}_to_${token}_via_${methodToken(creationMethod.name)}`;
|
|
13424
|
+
methods.push({
|
|
13425
|
+
name: viaName,
|
|
13426
|
+
docs: [
|
|
13427
|
+
`Prepare a reviewable artifact that will create a new ${classSummary.name} through ${creationMethod.name}, then move ${docsPrefix} to ${stateNameValue}.`
|
|
13428
|
+
],
|
|
13429
|
+
static: true,
|
|
13430
|
+
params: [
|
|
13431
|
+
{ name: "input", type: "Record<string, any>", optional: true }
|
|
13432
|
+
],
|
|
13433
|
+
returnType: "Promise<SessionArtifactRecord>",
|
|
13434
|
+
runtime: creationRuntime
|
|
13435
|
+
});
|
|
13436
|
+
if (creationMethods.length === 1) {
|
|
13437
|
+
methods.push({
|
|
13438
|
+
name: `prepare_${machine.name}_to_${token}`,
|
|
13439
|
+
docs: [
|
|
13440
|
+
`Prepare a reviewable artifact that will create a new ${classSummary.name}, then move ${docsPrefix} to ${stateNameValue}.`
|
|
13441
|
+
],
|
|
13442
|
+
static: true,
|
|
13443
|
+
params: [
|
|
13444
|
+
{ name: "input", type: "Record<string, any>", optional: true }
|
|
13445
|
+
],
|
|
13446
|
+
returnType: "Promise<SessionArtifactRecord>",
|
|
13447
|
+
runtime: creationRuntime
|
|
13448
|
+
});
|
|
13449
|
+
}
|
|
13450
|
+
}
|
|
13451
|
+
}
|
|
13452
|
+
return methods;
|
|
12748
13453
|
}
|
|
12749
13454
|
function readStateMachineSummaries(rawClass) {
|
|
12750
13455
|
return {
|
|
@@ -12767,8 +13472,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12767
13472
|
type StateMachineMutation {
|
|
12768
13473
|
name: String!
|
|
12769
13474
|
state_machine: StateMachine!
|
|
12770
|
-
add_state(name: String!, is_final: Boolean): StateMachineMutation!
|
|
12771
|
-
add_transition(name: String!, from: String!, to: String
|
|
13475
|
+
add_state(name: String!, is_final: Boolean, label: String, description: String): StateMachineMutation!
|
|
13476
|
+
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!
|
|
12772
13477
|
activate_transition(name: String!): StateMachineMutation!
|
|
12773
13478
|
}
|
|
12774
13479
|
|
|
@@ -12785,6 +13490,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12785
13490
|
type StateMachineSnapshotMutation {
|
|
12786
13491
|
snapshot: StateMachineSnapshot!
|
|
12787
13492
|
activate_transition(name: String!): StateMachineSnapshotMutation!
|
|
13493
|
+
observe_state(state: String!, force: Boolean, source: String): StateMachineSnapshotMutation!
|
|
12788
13494
|
}
|
|
12789
13495
|
|
|
12790
13496
|
type StateMachine {
|
|
@@ -12804,6 +13510,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12804
13510
|
|
|
12805
13511
|
type StateMachineState {
|
|
12806
13512
|
name: String!
|
|
13513
|
+
label: String
|
|
13514
|
+
description: String
|
|
12807
13515
|
is_final: Boolean!
|
|
12808
13516
|
}
|
|
12809
13517
|
|
|
@@ -12811,6 +13519,14 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12811
13519
|
name: String!
|
|
12812
13520
|
from: StateMachineState!
|
|
12813
13521
|
to: StateMachineState!
|
|
13522
|
+
label: String
|
|
13523
|
+
description: String
|
|
13524
|
+
action_json: String
|
|
13525
|
+
assignee_json: String
|
|
13526
|
+
requirements_json: String
|
|
13527
|
+
permission_json: String
|
|
13528
|
+
risk: String
|
|
13529
|
+
expected_outcome_json: String
|
|
12814
13530
|
}
|
|
12815
13531
|
|
|
12816
13532
|
type StateMachinePath {
|
|
@@ -12863,23 +13579,47 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12863
13579
|
StateMachineMutation: {
|
|
12864
13580
|
name: (value) => value.name,
|
|
12865
13581
|
state_machine: async (value) => await run(value.target.state_machine(value.name)),
|
|
12866
|
-
add_state: async (value, { name, is_final }) => {
|
|
13582
|
+
add_state: async (value, { name, is_final, label, description }) => {
|
|
12867
13583
|
await run(
|
|
12868
13584
|
value.target.add_state_machine_state(
|
|
12869
13585
|
value.name,
|
|
12870
13586
|
name,
|
|
12871
|
-
is_final ?? false
|
|
13587
|
+
is_final ?? false,
|
|
13588
|
+
label,
|
|
13589
|
+
description
|
|
12872
13590
|
)
|
|
12873
13591
|
);
|
|
12874
13592
|
return value;
|
|
12875
13593
|
},
|
|
12876
|
-
add_transition: async (value, {
|
|
13594
|
+
add_transition: async (value, {
|
|
13595
|
+
name,
|
|
13596
|
+
from: from2,
|
|
13597
|
+
to,
|
|
13598
|
+
label,
|
|
13599
|
+
description,
|
|
13600
|
+
action_json,
|
|
13601
|
+
assignee_json,
|
|
13602
|
+
requirements_json,
|
|
13603
|
+
permission_json,
|
|
13604
|
+
risk,
|
|
13605
|
+
expected_outcome_json
|
|
13606
|
+
}) => {
|
|
12877
13607
|
await run(
|
|
12878
13608
|
value.target.add_state_machine_transition(
|
|
12879
13609
|
value.name,
|
|
12880
13610
|
name,
|
|
12881
|
-
|
|
12882
|
-
to
|
|
13611
|
+
from2,
|
|
13612
|
+
to,
|
|
13613
|
+
{
|
|
13614
|
+
label,
|
|
13615
|
+
description,
|
|
13616
|
+
actionJson: action_json,
|
|
13617
|
+
assigneeJson: assignee_json,
|
|
13618
|
+
requirementsJson: requirements_json,
|
|
13619
|
+
permissionJson: permission_json,
|
|
13620
|
+
risk,
|
|
13621
|
+
expectedOutcomeJson: expected_outcome_json
|
|
13622
|
+
}
|
|
12883
13623
|
)
|
|
12884
13624
|
);
|
|
12885
13625
|
return value;
|
|
@@ -12898,16 +13638,37 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12898
13638
|
value.target.activate_state_machine_transition(value.name, name)
|
|
12899
13639
|
);
|
|
12900
13640
|
return value;
|
|
13641
|
+
},
|
|
13642
|
+
observe_state: async (value, { state, force, source }) => {
|
|
13643
|
+
await run(
|
|
13644
|
+
value.target.observe_state_machine_state(
|
|
13645
|
+
value.name,
|
|
13646
|
+
state,
|
|
13647
|
+
force === true,
|
|
13648
|
+
source
|
|
13649
|
+
)
|
|
13650
|
+
);
|
|
13651
|
+
return value;
|
|
12901
13652
|
}
|
|
12902
13653
|
},
|
|
12903
13654
|
StateMachineState: {
|
|
12904
13655
|
name: (value) => value.name,
|
|
13656
|
+
label: (value) => value.label || null,
|
|
13657
|
+
description: (value) => value.description || null,
|
|
12905
13658
|
is_final: (value) => value.is_final
|
|
12906
13659
|
},
|
|
12907
13660
|
StateMachineTransition: {
|
|
12908
13661
|
name: (value) => value.name,
|
|
12909
13662
|
from: (value) => value.from_state || { name: value.from, is_final: false },
|
|
12910
|
-
to: (value) => value.to_state || { name: value.to, is_final: false }
|
|
13663
|
+
to: (value) => value.to_state || { name: value.to, is_final: false },
|
|
13664
|
+
label: (value) => value.label || null,
|
|
13665
|
+
description: (value) => value.description || null,
|
|
13666
|
+
action_json: (value) => value.action_json || null,
|
|
13667
|
+
assignee_json: (value) => value.assignee_json || null,
|
|
13668
|
+
requirements_json: (value) => value.requirements_json || null,
|
|
13669
|
+
permission_json: (value) => value.permission_json || null,
|
|
13670
|
+
risk: (value) => value.risk || null,
|
|
13671
|
+
expected_outcome_json: (value) => value.expected_outcome_json || null
|
|
12911
13672
|
},
|
|
12912
13673
|
StateMachinePath: {
|
|
12913
13674
|
states: (value) => value.states,
|
|
@@ -12974,6 +13735,14 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
12974
13735
|
name
|
|
12975
13736
|
from { name }
|
|
12976
13737
|
to { name }
|
|
13738
|
+
label
|
|
13739
|
+
description
|
|
13740
|
+
action_json
|
|
13741
|
+
assignee_json
|
|
13742
|
+
requirements_json
|
|
13743
|
+
permission_json
|
|
13744
|
+
risk
|
|
13745
|
+
expected_outcome_json
|
|
12977
13746
|
}
|
|
12978
13747
|
}`
|
|
12979
13748
|
]
|
|
@@ -13005,6 +13774,104 @@ function describeRule(rule) {
|
|
|
13005
13774
|
return `${rule.operator} ${String(rule.booleanValue)}`;
|
|
13006
13775
|
return rule.operator;
|
|
13007
13776
|
}
|
|
13777
|
+
function valueAsString(value) {
|
|
13778
|
+
if (typeof value === "string") return value;
|
|
13779
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
13780
|
+
return String(value);
|
|
13781
|
+
}
|
|
13782
|
+
return "";
|
|
13783
|
+
}
|
|
13784
|
+
function valueAsNumber(value) {
|
|
13785
|
+
if (typeof value === "number") return value;
|
|
13786
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
13787
|
+
const parsed = Number(value);
|
|
13788
|
+
return Number.isFinite(parsed) ? parsed : NaN;
|
|
13789
|
+
}
|
|
13790
|
+
return NaN;
|
|
13791
|
+
}
|
|
13792
|
+
function ruleStringValue(rule) {
|
|
13793
|
+
return rule.stringValue ?? rule.string_value ?? "";
|
|
13794
|
+
}
|
|
13795
|
+
function ruleNumberValue(rule) {
|
|
13796
|
+
return rule.numberValue ?? rule.number_value;
|
|
13797
|
+
}
|
|
13798
|
+
function ruleBooleanValue(rule) {
|
|
13799
|
+
return rule.booleanValue ?? rule.boolean_value;
|
|
13800
|
+
}
|
|
13801
|
+
function evaluateValidationRule(value, rule) {
|
|
13802
|
+
const operator = typeof rule.operator === "string" ? rule.operator : "";
|
|
13803
|
+
const stringValue2 = ruleStringValue(rule);
|
|
13804
|
+
const numberValue = ruleNumberValue(rule);
|
|
13805
|
+
const booleanValue = ruleBooleanValue(rule);
|
|
13806
|
+
let passed = true;
|
|
13807
|
+
switch (operator) {
|
|
13808
|
+
case "eq":
|
|
13809
|
+
if (numberValue !== void 0) {
|
|
13810
|
+
passed = valueAsNumber(value) === numberValue;
|
|
13811
|
+
} else if (booleanValue !== void 0) {
|
|
13812
|
+
passed = value === booleanValue;
|
|
13813
|
+
} else {
|
|
13814
|
+
passed = valueAsString(value) === stringValue2;
|
|
13815
|
+
}
|
|
13816
|
+
break;
|
|
13817
|
+
case "neq":
|
|
13818
|
+
if (numberValue !== void 0) {
|
|
13819
|
+
passed = valueAsNumber(value) !== numberValue;
|
|
13820
|
+
} else if (booleanValue !== void 0) {
|
|
13821
|
+
passed = value !== booleanValue;
|
|
13822
|
+
} else {
|
|
13823
|
+
passed = valueAsString(value) !== stringValue2;
|
|
13824
|
+
}
|
|
13825
|
+
break;
|
|
13826
|
+
case "gt":
|
|
13827
|
+
passed = valueAsNumber(value) > (numberValue ?? NaN);
|
|
13828
|
+
break;
|
|
13829
|
+
case "gte":
|
|
13830
|
+
passed = valueAsNumber(value) >= (numberValue ?? NaN);
|
|
13831
|
+
break;
|
|
13832
|
+
case "lt":
|
|
13833
|
+
passed = valueAsNumber(value) < (numberValue ?? NaN);
|
|
13834
|
+
break;
|
|
13835
|
+
case "lte":
|
|
13836
|
+
passed = valueAsNumber(value) <= (numberValue ?? NaN);
|
|
13837
|
+
break;
|
|
13838
|
+
case "true":
|
|
13839
|
+
passed = value === true;
|
|
13840
|
+
break;
|
|
13841
|
+
case "false":
|
|
13842
|
+
passed = value === false;
|
|
13843
|
+
break;
|
|
13844
|
+
case "regex":
|
|
13845
|
+
try {
|
|
13846
|
+
passed = new RegExp(stringValue2).test(valueAsString(value));
|
|
13847
|
+
} catch {
|
|
13848
|
+
passed = false;
|
|
13849
|
+
}
|
|
13850
|
+
break;
|
|
13851
|
+
case "contains":
|
|
13852
|
+
passed = valueAsString(value).includes(stringValue2);
|
|
13853
|
+
break;
|
|
13854
|
+
case "not_contains":
|
|
13855
|
+
passed = !valueAsString(value).includes(stringValue2);
|
|
13856
|
+
break;
|
|
13857
|
+
case "starts_with":
|
|
13858
|
+
passed = valueAsString(value).startsWith(stringValue2);
|
|
13859
|
+
break;
|
|
13860
|
+
case "ends_with":
|
|
13861
|
+
passed = valueAsString(value).endsWith(stringValue2);
|
|
13862
|
+
break;
|
|
13863
|
+
default:
|
|
13864
|
+
passed = true;
|
|
13865
|
+
}
|
|
13866
|
+
return {
|
|
13867
|
+
passed,
|
|
13868
|
+
operator,
|
|
13869
|
+
message: rule.message ?? null
|
|
13870
|
+
};
|
|
13871
|
+
}
|
|
13872
|
+
function validationRuleFailureMessage(path, rule) {
|
|
13873
|
+
return rule.message || `${path} failed ${rule.operator} validation`;
|
|
13874
|
+
}
|
|
13008
13875
|
function normalizeRule(rule) {
|
|
13009
13876
|
const operator = typeof rule?.operator === "string" ? rule.operator : "";
|
|
13010
13877
|
if (operator.length === 0) return null;
|
|
@@ -13191,6 +14058,18 @@ function buildEffectMetamodelMutations(toolPath, spec) {
|
|
|
13191
14058
|
}
|
|
13192
14059
|
|
|
13193
14060
|
// src/client.ts
|
|
14061
|
+
var DEFAULT_CONVERSATION_SESSION_LIST_LIMIT = 100;
|
|
14062
|
+
var MAX_CONVERSATION_SESSION_LIST_LIMIT = 500;
|
|
14063
|
+
var MAX_CONVERSATION_SESSION_LIST_OFFSET = 1e5;
|
|
14064
|
+
function boundedSessionListInteger(value, name, fallback, minimum, maximum) {
|
|
14065
|
+
if (value === void 0) return fallback;
|
|
14066
|
+
if (!Number.isInteger(value) || value < minimum || value > maximum) {
|
|
14067
|
+
throw new RangeError(
|
|
14068
|
+
`Session list ${name} must be an integer between ${minimum} and ${maximum}.`
|
|
14069
|
+
);
|
|
14070
|
+
}
|
|
14071
|
+
return value;
|
|
14072
|
+
}
|
|
13194
14073
|
var STANDARD_MODULES_OPERATIONS = [
|
|
13195
14074
|
{
|
|
13196
14075
|
create: "entity",
|
|
@@ -13227,6 +14106,26 @@ var STANDARD_MODULES_OPERATIONS = [
|
|
|
13227
14106
|
var BUILTIN_MODULES = {
|
|
13228
14107
|
standard_modules: STANDARD_MODULES_OPERATIONS
|
|
13229
14108
|
};
|
|
14109
|
+
function stateNameFromMethodName(methodName) {
|
|
14110
|
+
const raw = methodName.startsWith("to") ? methodName.slice(2) : methodName;
|
|
14111
|
+
return raw.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
14112
|
+
}
|
|
14113
|
+
function appendQueryOptions(searchParams, query) {
|
|
14114
|
+
for (const [key, value] of Object.entries(query || {})) {
|
|
14115
|
+
if (value === null || typeof value === "undefined" || value === "") {
|
|
14116
|
+
continue;
|
|
14117
|
+
}
|
|
14118
|
+
if (value instanceof Date) {
|
|
14119
|
+
searchParams.set(key, value.toISOString());
|
|
14120
|
+
continue;
|
|
14121
|
+
}
|
|
14122
|
+
if (Array.isArray(value)) {
|
|
14123
|
+
if (value.length > 0) searchParams.set(key, value.join(","));
|
|
14124
|
+
continue;
|
|
14125
|
+
}
|
|
14126
|
+
searchParams.set(key, String(value));
|
|
14127
|
+
}
|
|
14128
|
+
}
|
|
13230
14129
|
var DEFAULT_DIRECT_RECORD_OBJECTS_REQUEST_BATCH_SIZE = 100;
|
|
13231
14130
|
var MAX_RECORD_OBJECTS_CONCURRENCY = 16;
|
|
13232
14131
|
var DEFAULT_DIRECT_RECORD_OBJECTS_RETRY_COUNT = 3;
|
|
@@ -13257,8 +14156,20 @@ function bodyInitFromSessionFileUpload(body) {
|
|
|
13257
14156
|
return body;
|
|
13258
14157
|
}
|
|
13259
14158
|
var EFFECT_CATALOG_SYNC_TIMEOUT_MS = 12e4;
|
|
14159
|
+
var EFFECT_CATALOG_SYNC_BATCH_SIZE = 20;
|
|
13260
14160
|
var EFFECT_CATALOG_SYNC_RETRY_COUNT = 3;
|
|
13261
14161
|
var EFFECT_CATALOG_SYNC_RETRY_DELAY_MS = 1e3;
|
|
14162
|
+
function chunkItems(items, batchSize) {
|
|
14163
|
+
const chunks = [];
|
|
14164
|
+
for (let offset = 0; offset < items.length; offset += batchSize) {
|
|
14165
|
+
chunks.push(items.slice(offset, offset + batchSize));
|
|
14166
|
+
}
|
|
14167
|
+
return chunks;
|
|
14168
|
+
}
|
|
14169
|
+
function isUnsupportedEffectCatalogMutation(error) {
|
|
14170
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
14171
|
+
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");
|
|
14172
|
+
}
|
|
13262
14173
|
function planRecordObjectsChunks(records, batchSize) {
|
|
13263
14174
|
const total = records.length;
|
|
13264
14175
|
const size = Math.max(1, Math.min(batchSize, total));
|
|
@@ -13270,6 +14181,23 @@ function planRecordObjectsChunks(records, batchSize) {
|
|
|
13270
14181
|
}
|
|
13271
14182
|
return plans;
|
|
13272
14183
|
}
|
|
14184
|
+
function preserveRecordObjectRealId(record) {
|
|
14185
|
+
const realId = record.id.trim();
|
|
14186
|
+
if (!realId) {
|
|
14187
|
+
return record;
|
|
14188
|
+
}
|
|
14189
|
+
const fields = record.fields || {};
|
|
14190
|
+
if (typeof fields.real_id === "string" && fields.real_id.trim()) {
|
|
14191
|
+
return record;
|
|
14192
|
+
}
|
|
14193
|
+
return {
|
|
14194
|
+
...record,
|
|
14195
|
+
fields: {
|
|
14196
|
+
...fields,
|
|
14197
|
+
real_id: realId
|
|
14198
|
+
}
|
|
14199
|
+
};
|
|
14200
|
+
}
|
|
13273
14201
|
function computeEffectKey2(effect) {
|
|
13274
14202
|
const attachedClass = effect.className?.trim();
|
|
13275
14203
|
if (!attachedClass) {
|
|
@@ -13480,7 +14408,7 @@ var Environment = class _Environment {
|
|
|
13480
14408
|
}
|
|
13481
14409
|
get sessions() {
|
|
13482
14410
|
return {
|
|
13483
|
-
list: async (options) => this.listSessions(options
|
|
14411
|
+
list: async (options = {}) => this.listSessions(options),
|
|
13484
14412
|
create: async (options) => this.createSession(options),
|
|
13485
14413
|
connect: async (sessionId, options) => this.connectSession(sessionId, options),
|
|
13486
14414
|
reopen: async (sessionId, options) => this.reopenSession(sessionId, options),
|
|
@@ -13507,11 +14435,105 @@ var Environment = class _Environment {
|
|
|
13507
14435
|
getAwaitingCount: async () => this.getAwaitingRecordCount()
|
|
13508
14436
|
};
|
|
13509
14437
|
}
|
|
14438
|
+
/**
|
|
14439
|
+
* Mirror product-owned workflow state into Granular without making Granular
|
|
14440
|
+
* own the customer application's state machine.
|
|
14441
|
+
*/
|
|
14442
|
+
async recordState(input) {
|
|
14443
|
+
const { machine, state, ...target } = input;
|
|
14444
|
+
if (!machine.trim()) {
|
|
14445
|
+
throw new Error("State update requires a machine name");
|
|
14446
|
+
}
|
|
14447
|
+
if (!state.trim()) {
|
|
14448
|
+
throw new Error("State update requires a state");
|
|
14449
|
+
}
|
|
14450
|
+
return this.recordObject({
|
|
14451
|
+
className: target.className,
|
|
14452
|
+
id: target.id,
|
|
14453
|
+
...target.label ? { label: target.label } : {},
|
|
14454
|
+
...target.fields ? { fields: target.fields } : {},
|
|
14455
|
+
...target.relationships ? { relationships: target.relationships } : {},
|
|
14456
|
+
states: {
|
|
14457
|
+
[machine.trim()]: {
|
|
14458
|
+
state: state.trim(),
|
|
14459
|
+
...target.source ? { source: target.source } : {},
|
|
14460
|
+
...target.cause ? { cause: target.cause } : {},
|
|
14461
|
+
...target.actorId ? { actorId: target.actorId } : {},
|
|
14462
|
+
...target.observedAt !== void 0 ? { observedAt: target.observedAt } : {},
|
|
14463
|
+
...target.force !== void 0 ? { force: target.force } : {},
|
|
14464
|
+
...target.metadata ? { metadata: target.metadata } : {}
|
|
14465
|
+
}
|
|
14466
|
+
}
|
|
14467
|
+
});
|
|
14468
|
+
}
|
|
14469
|
+
/**
|
|
14470
|
+
* Mirror product-owned workflow state into Granular without making Granular
|
|
14471
|
+
* own the customer application's state machine.
|
|
14472
|
+
*
|
|
14473
|
+
* Example:
|
|
14474
|
+
* `await env.recordState({ className: "spend_request", id, machine: "lifecycle", state: "policy_review", source: "customer_backend" })`
|
|
14475
|
+
*/
|
|
14476
|
+
state(target) {
|
|
14477
|
+
const observe = async (machineName, stateName, input = {}) => {
|
|
14478
|
+
const observedState = input.observedState || input.state || stateName;
|
|
14479
|
+
if (!observedState) {
|
|
14480
|
+
throw new Error("State observation requires a target state");
|
|
14481
|
+
}
|
|
14482
|
+
return this.recordState({
|
|
14483
|
+
...target,
|
|
14484
|
+
machine: machineName,
|
|
14485
|
+
state: observedState,
|
|
14486
|
+
...input.source ? { source: input.source } : {},
|
|
14487
|
+
...input.cause ? { cause: input.cause } : {},
|
|
14488
|
+
...input.actorId ? { actorId: input.actorId } : {},
|
|
14489
|
+
...input.observedAt !== void 0 ? { observedAt: input.observedAt } : {},
|
|
14490
|
+
...input.force !== void 0 ? { force: input.force } : {},
|
|
14491
|
+
...input.metadata ? { metadata: input.metadata } : {}
|
|
14492
|
+
});
|
|
14493
|
+
};
|
|
14494
|
+
return new Proxy(
|
|
14495
|
+
{},
|
|
14496
|
+
{
|
|
14497
|
+
get: (_target, machineProperty) => {
|
|
14498
|
+
if (typeof machineProperty !== "string") return void 0;
|
|
14499
|
+
return new Proxy(
|
|
14500
|
+
{},
|
|
14501
|
+
{
|
|
14502
|
+
get: (_machineTarget, stateProperty) => {
|
|
14503
|
+
if (stateProperty === "to") {
|
|
14504
|
+
return (stateName, input) => observe(machineProperty, stateName, input || {});
|
|
14505
|
+
}
|
|
14506
|
+
if (typeof stateProperty !== "string") return void 0;
|
|
14507
|
+
return (input) => observe(
|
|
14508
|
+
machineProperty,
|
|
14509
|
+
stateNameFromMethodName(stateProperty),
|
|
14510
|
+
input || {}
|
|
14511
|
+
);
|
|
14512
|
+
}
|
|
14513
|
+
}
|
|
14514
|
+
);
|
|
14515
|
+
}
|
|
14516
|
+
}
|
|
14517
|
+
);
|
|
14518
|
+
}
|
|
13510
14519
|
get feedback() {
|
|
13511
14520
|
return {
|
|
13512
14521
|
list: async () => this.listFeedback()
|
|
13513
14522
|
};
|
|
13514
14523
|
}
|
|
14524
|
+
get manualActions() {
|
|
14525
|
+
return {
|
|
14526
|
+
record: (input) => this.recordManualAction(input),
|
|
14527
|
+
list: (options = {}) => this.listManualActions(options),
|
|
14528
|
+
suggest: (options = {}) => this.suggestManualActions(options)
|
|
14529
|
+
};
|
|
14530
|
+
}
|
|
14531
|
+
get artifactApprovals() {
|
|
14532
|
+
return {
|
|
14533
|
+
list: (options = {}) => this.listArtifactApprovals(options),
|
|
14534
|
+
decide: (approvalTaskId, input) => this.decideArtifactApproval(approvalTaskId, input)
|
|
14535
|
+
};
|
|
14536
|
+
}
|
|
13515
14537
|
/**
|
|
13516
14538
|
* Sessionless environments do not own a live transport, so disconnecting the
|
|
13517
14539
|
* environment handle itself is a no-op. This keeps the public surface
|
|
@@ -13521,17 +14543,12 @@ var Environment = class _Environment {
|
|
|
13521
14543
|
*/
|
|
13522
14544
|
async disconnect() {
|
|
13523
14545
|
}
|
|
13524
|
-
async listSessions(
|
|
13525
|
-
|
|
13526
|
-
|
|
13527
|
-
|
|
13528
|
-
|
|
13529
|
-
|
|
13530
|
-
return [...active, ...closed].sort(
|
|
13531
|
-
(left, right) => Date.parse(right.lastSeenAt) - Date.parse(left.lastSeenAt)
|
|
13532
|
-
);
|
|
13533
|
-
}
|
|
13534
|
-
return status === "closed" ? this.granular.listClosedSessions({ environmentId: this.environmentId }) : this.granular.listOpenSessions({ environmentId: this.environmentId });
|
|
14546
|
+
async listSessions(optionsOrStatus = {}) {
|
|
14547
|
+
const options = typeof optionsOrStatus === "string" ? { status: optionsOrStatus } : optionsOrStatus;
|
|
14548
|
+
return this.granular.listSessions({
|
|
14549
|
+
...options,
|
|
14550
|
+
environmentId: this.environmentId
|
|
14551
|
+
});
|
|
13535
14552
|
}
|
|
13536
14553
|
async getUserEnvironmentState(options = {}) {
|
|
13537
14554
|
return this.granular.getUserEnvironmentState({
|
|
@@ -13549,6 +14566,7 @@ var Environment = class _Environment {
|
|
|
13549
14566
|
return this.granular.createSession({
|
|
13550
14567
|
environmentId: this.environmentId,
|
|
13551
14568
|
clientId: options?.clientId,
|
|
14569
|
+
sessionScope: options?.sessionScope,
|
|
13552
14570
|
initialHeap: options?.initialHeap
|
|
13553
14571
|
});
|
|
13554
14572
|
}
|
|
@@ -13590,6 +14608,50 @@ var Environment = class _Environment {
|
|
|
13590
14608
|
const response = await this.controlPlaneRequest(`/control/environments/${this.environmentId}/feedback`);
|
|
13591
14609
|
return Array.isArray(response.items) ? response.items : [];
|
|
13592
14610
|
}
|
|
14611
|
+
async recordManualAction(input) {
|
|
14612
|
+
const body = {
|
|
14613
|
+
...input,
|
|
14614
|
+
...input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {}
|
|
14615
|
+
};
|
|
14616
|
+
return this.controlPlaneRequest(
|
|
14617
|
+
`/control/environments/${this.environmentId}/manual-actions`,
|
|
14618
|
+
{
|
|
14619
|
+
method: "POST",
|
|
14620
|
+
body: JSON.stringify(body)
|
|
14621
|
+
}
|
|
14622
|
+
);
|
|
14623
|
+
}
|
|
14624
|
+
async listManualActions(options = {}) {
|
|
14625
|
+
const query = new URLSearchParams();
|
|
14626
|
+
appendQueryOptions(query, options);
|
|
14627
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
14628
|
+
return this.controlPlaneRequest(`/control/environments/${this.environmentId}/manual-actions${suffix}`);
|
|
14629
|
+
}
|
|
14630
|
+
async suggestManualActions(options = {}) {
|
|
14631
|
+
const query = new URLSearchParams();
|
|
14632
|
+
appendQueryOptions(query, options);
|
|
14633
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
14634
|
+
return this.controlPlaneRequest(
|
|
14635
|
+
`/control/environments/${this.environmentId}/manual-actions/suggestions${suffix}`
|
|
14636
|
+
);
|
|
14637
|
+
}
|
|
14638
|
+
async listArtifactApprovals(options = {}) {
|
|
14639
|
+
const query = new URLSearchParams();
|
|
14640
|
+
appendQueryOptions(query, options);
|
|
14641
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
14642
|
+
return this.controlPlaneRequest(
|
|
14643
|
+
`/control/environments/${this.environmentId}/artifact-approvals${suffix}`
|
|
14644
|
+
);
|
|
14645
|
+
}
|
|
14646
|
+
async decideArtifactApproval(approvalTaskId, input) {
|
|
14647
|
+
return this.controlPlaneRequest(
|
|
14648
|
+
`/control/environments/${this.environmentId}/artifact-approvals/${encodeURIComponent(approvalTaskId)}/decide`,
|
|
14649
|
+
{
|
|
14650
|
+
method: "POST",
|
|
14651
|
+
body: JSON.stringify(input)
|
|
14652
|
+
}
|
|
14653
|
+
);
|
|
14654
|
+
}
|
|
13593
14655
|
getRuntimeBaseUrl() {
|
|
13594
14656
|
return deriveRuntimeBaseUrl(this._apiEndpoint);
|
|
13595
14657
|
}
|
|
@@ -14414,10 +15476,11 @@ var Environment = class _Environment {
|
|
|
14414
15476
|
if (!Array.isArray(records) || records.length === 0) {
|
|
14415
15477
|
return [];
|
|
14416
15478
|
}
|
|
15479
|
+
const recordsToWrite = records.map(preserveRecordObjectRealId);
|
|
14417
15480
|
const batchSize = Math.max(
|
|
14418
15481
|
1,
|
|
14419
15482
|
Math.min(
|
|
14420
|
-
|
|
15483
|
+
recordsToWrite.length,
|
|
14421
15484
|
options?.batchSize ?? DEFAULT_DIRECT_RECORD_OBJECTS_REQUEST_BATCH_SIZE
|
|
14422
15485
|
)
|
|
14423
15486
|
);
|
|
@@ -14425,8 +15488,8 @@ var Environment = class _Environment {
|
|
|
14425
15488
|
MAX_RECORD_OBJECTS_CONCURRENCY,
|
|
14426
15489
|
Math.max(1, options?.concurrency ?? 1)
|
|
14427
15490
|
);
|
|
14428
|
-
const plans = planRecordObjectsChunks(
|
|
14429
|
-
const total =
|
|
15491
|
+
const plans = planRecordObjectsChunks(recordsToWrite, batchSize);
|
|
15492
|
+
const total = recordsToWrite.length;
|
|
14430
15493
|
const results = new Array(total);
|
|
14431
15494
|
const onChunk = options?.onChunkComplete;
|
|
14432
15495
|
for (let waveStart = 0; waveStart < plans.length; waveStart += concurrency) {
|
|
@@ -14497,12 +15560,13 @@ var Environment = class _Environment {
|
|
|
14497
15560
|
* synchronous upserts and fine-grained chunk progress via **`onChunkComplete`**.
|
|
14498
15561
|
*/
|
|
14499
15562
|
async enqueueRecordImport(records, options = {}) {
|
|
15563
|
+
const recordsToImport = records.map(preserveRecordObjectRealId);
|
|
14500
15564
|
return this.controlPlaneRequest(
|
|
14501
15565
|
`/control/environments/${this.environmentId}/record-imports`,
|
|
14502
15566
|
{
|
|
14503
15567
|
method: "POST",
|
|
14504
15568
|
body: JSON.stringify({
|
|
14505
|
-
records,
|
|
15569
|
+
records: recordsToImport,
|
|
14506
15570
|
batchSize: options.batchSize,
|
|
14507
15571
|
setupRunId: options.setupRunId,
|
|
14508
15572
|
writeMode: options.writeMode
|
|
@@ -14610,11 +15674,7 @@ var EnvironmentSession = class extends Session {
|
|
|
14610
15674
|
}
|
|
14611
15675
|
buildSessionDataUrl(path, query) {
|
|
14612
15676
|
const searchParams = new URLSearchParams();
|
|
14613
|
-
|
|
14614
|
-
if (value !== null && typeof value !== "undefined" && value !== "") {
|
|
14615
|
-
searchParams.set(key, String(value));
|
|
14616
|
-
}
|
|
14617
|
-
}
|
|
15677
|
+
appendQueryOptions(searchParams, query);
|
|
14618
15678
|
const queryString = searchParams.toString();
|
|
14619
15679
|
return `${this.environment.runtimeBaseUrl}${this.sessionDataRoutePrefix}/${encodeURIComponent(this.sessionId)}${path}${queryString ? `?${queryString}` : ""}`;
|
|
14620
15680
|
}
|
|
@@ -14697,9 +15757,108 @@ var EnvironmentSession = class extends Session {
|
|
|
14697
15757
|
),
|
|
14698
15758
|
get: (jobId) => this.sessionDataRequest(
|
|
14699
15759
|
`/jobs/${encodeURIComponent(jobId)}`
|
|
15760
|
+
),
|
|
15761
|
+
latest: async (options = {}) => {
|
|
15762
|
+
const page = await this.sessionDataRequest("/jobs", {
|
|
15763
|
+
status: options.status || "all",
|
|
15764
|
+
latest: true,
|
|
15765
|
+
limit: 1
|
|
15766
|
+
});
|
|
15767
|
+
return page.items[0] || null;
|
|
15768
|
+
}
|
|
15769
|
+
};
|
|
15770
|
+
}
|
|
15771
|
+
get artifacts() {
|
|
15772
|
+
return {
|
|
15773
|
+
list: (options = {}) => {
|
|
15774
|
+
const queryOptions = { ...options };
|
|
15775
|
+
if (options.target) {
|
|
15776
|
+
queryOptions.targetClassName = options.target.className;
|
|
15777
|
+
queryOptions.targetId = options.target.id;
|
|
15778
|
+
delete queryOptions.target;
|
|
15779
|
+
}
|
|
15780
|
+
return this.sessionDataRequest("/artifacts", queryOptions);
|
|
15781
|
+
},
|
|
15782
|
+
listForLatestJob: (options = {}) => this.artifacts.list({
|
|
15783
|
+
...options,
|
|
15784
|
+
latestJob: true
|
|
15785
|
+
}),
|
|
15786
|
+
get: (artifactId) => this.sessionDataRequest(
|
|
15787
|
+
`/artifacts/${encodeURIComponent(artifactId)}`
|
|
15788
|
+
),
|
|
15789
|
+
create: (artifact) => this.sessionDataRequest(
|
|
15790
|
+
"/artifacts",
|
|
15791
|
+
void 0,
|
|
15792
|
+
{
|
|
15793
|
+
method: "POST",
|
|
15794
|
+
body: artifact
|
|
15795
|
+
}
|
|
15796
|
+
),
|
|
15797
|
+
updateInputs: (artifactId, patch) => this.sessionDataRequest(
|
|
15798
|
+
`/artifacts/${encodeURIComponent(artifactId)}`,
|
|
15799
|
+
void 0,
|
|
15800
|
+
{
|
|
15801
|
+
method: "PATCH",
|
|
15802
|
+
body: patch
|
|
15803
|
+
}
|
|
15804
|
+
),
|
|
15805
|
+
validate: (artifactId) => this.sessionDataRequest(
|
|
15806
|
+
`/artifacts/${encodeURIComponent(artifactId)}/validate`,
|
|
15807
|
+
void 0,
|
|
15808
|
+
{ method: "POST" }
|
|
15809
|
+
),
|
|
15810
|
+
execute: (artifactId, options) => this.sessionDataRequest(
|
|
15811
|
+
`/artifacts/${encodeURIComponent(artifactId)}/execute`,
|
|
15812
|
+
void 0,
|
|
15813
|
+
{ method: "POST", body: options }
|
|
15814
|
+
),
|
|
15815
|
+
approve: (artifactId, options) => this.sessionDataRequest(
|
|
15816
|
+
`/artifacts/${encodeURIComponent(artifactId)}/approve`,
|
|
15817
|
+
void 0,
|
|
15818
|
+
{ method: "POST", body: options }
|
|
15819
|
+
),
|
|
15820
|
+
cancel: (artifactId) => this.sessionDataRequest(
|
|
15821
|
+
`/artifacts/${encodeURIComponent(artifactId)}/cancel`,
|
|
15822
|
+
void 0,
|
|
15823
|
+
{ method: "POST" }
|
|
14700
15824
|
)
|
|
14701
15825
|
};
|
|
14702
15826
|
}
|
|
15827
|
+
get manualActions() {
|
|
15828
|
+
const useDelegatedBrowserRoute = this.sessionDataRoutePrefix === "/sdk/browser-sessions";
|
|
15829
|
+
return {
|
|
15830
|
+
record: (input) => useDelegatedBrowserRoute ? this.sessionDataRequest(
|
|
15831
|
+
"/manual-actions",
|
|
15832
|
+
void 0,
|
|
15833
|
+
{
|
|
15834
|
+
method: "POST",
|
|
15835
|
+
body: { ...input, sessionId: this.sessionId }
|
|
15836
|
+
}
|
|
15837
|
+
) : this.environment.manualActions.record({
|
|
15838
|
+
...input,
|
|
15839
|
+
sessionId: this.sessionId
|
|
15840
|
+
}),
|
|
15841
|
+
list: (options = {}) => useDelegatedBrowserRoute ? this.sessionDataRequest("/manual-actions", { ...options, sessionId: this.sessionId }) : this.environment.manualActions.list({
|
|
15842
|
+
...options,
|
|
15843
|
+
sessionId: this.sessionId
|
|
15844
|
+
}),
|
|
15845
|
+
suggest: (options = {}) => useDelegatedBrowserRoute ? this.sessionDataRequest(
|
|
15846
|
+
"/manual-actions/suggestions",
|
|
15847
|
+
options
|
|
15848
|
+
) : this.environment.manualActions.suggest(options)
|
|
15849
|
+
};
|
|
15850
|
+
}
|
|
15851
|
+
get artifactApprovals() {
|
|
15852
|
+
const useDelegatedBrowserRoute = this.sessionDataRoutePrefix === "/sdk/browser-sessions";
|
|
15853
|
+
return {
|
|
15854
|
+
list: (options = {}) => useDelegatedBrowserRoute ? this.sessionDataRequest("/artifact-approvals", options) : this.environment.artifactApprovals.list(options),
|
|
15855
|
+
decide: (approvalTaskId, input) => useDelegatedBrowserRoute ? this.sessionDataRequest(
|
|
15856
|
+
`/artifact-approvals/${encodeURIComponent(approvalTaskId)}/decide`,
|
|
15857
|
+
void 0,
|
|
15858
|
+
{ method: "POST", body: input }
|
|
15859
|
+
) : this.environment.artifactApprovals.decide(approvalTaskId, input)
|
|
15860
|
+
};
|
|
15861
|
+
}
|
|
14703
15862
|
get files() {
|
|
14704
15863
|
return {
|
|
14705
15864
|
list: (options = {}) => this.sessionDataRequest(
|
|
@@ -14780,13 +15939,16 @@ var EnvironmentSession = class extends Session {
|
|
|
14780
15939
|
get transcript() {
|
|
14781
15940
|
return {
|
|
14782
15941
|
list: async (options = {}) => {
|
|
14783
|
-
const [messages, jobs, entries, lists] = await Promise.all([
|
|
15942
|
+
const [messages, jobs, entries, lists, artifacts] = await Promise.all([
|
|
14784
15943
|
this.collectAllSessionItems(this.messages.list),
|
|
14785
15944
|
this.collectAllSessionItems(
|
|
14786
15945
|
(pageOptions) => this.jobs.list({ ...pageOptions, status: "all" })
|
|
14787
15946
|
),
|
|
14788
15947
|
this.collectAllSessionItems(this.heap.entries.list),
|
|
14789
|
-
this.collectAllSessionItems(this.heap.lists.list)
|
|
15948
|
+
this.collectAllSessionItems(this.heap.lists.list),
|
|
15949
|
+
this.collectAllSessionItems(
|
|
15950
|
+
(pageOptions) => this.artifacts.list({ ...pageOptions, status: "all" })
|
|
15951
|
+
)
|
|
14790
15952
|
]);
|
|
14791
15953
|
const liveDoc = {
|
|
14792
15954
|
conversation: { messages },
|
|
@@ -14800,6 +15962,21 @@ var EnvironmentSession = class extends Session {
|
|
|
14800
15962
|
(entry) => Boolean(entry)
|
|
14801
15963
|
)
|
|
14802
15964
|
)
|
|
15965
|
+
},
|
|
15966
|
+
artifacts: {
|
|
15967
|
+
byId: Object.fromEntries(
|
|
15968
|
+
artifacts.map((artifact) => {
|
|
15969
|
+
return artifact?.artifactId ? [
|
|
15970
|
+
artifact.artifactId,
|
|
15971
|
+
artifact
|
|
15972
|
+
] : null;
|
|
15973
|
+
}).filter(
|
|
15974
|
+
(entry) => Boolean(entry)
|
|
15975
|
+
)
|
|
15976
|
+
),
|
|
15977
|
+
order: artifacts.map((artifact) => artifact?.artifactId).filter(
|
|
15978
|
+
(artifactId) => Boolean(artifactId)
|
|
15979
|
+
)
|
|
14803
15980
|
}
|
|
14804
15981
|
};
|
|
14805
15982
|
const heap = normalizeHeapSnapshot({
|
|
@@ -14876,6 +16053,12 @@ var EnvironmentSession = class extends Session {
|
|
|
14876
16053
|
async recordObject(options) {
|
|
14877
16054
|
return this.environment.recordObject(options);
|
|
14878
16055
|
}
|
|
16056
|
+
async recordState(input) {
|
|
16057
|
+
return this.environment.recordState(input);
|
|
16058
|
+
}
|
|
16059
|
+
state(target) {
|
|
16060
|
+
return this.environment.state(target);
|
|
16061
|
+
}
|
|
14879
16062
|
async recordObjects(records, options) {
|
|
14880
16063
|
return this.environment.recordObjects(records, options);
|
|
14881
16064
|
}
|
|
@@ -14946,7 +16129,7 @@ var EnvironmentSession = class extends Session {
|
|
|
14946
16129
|
* Close only the socket transport without sending `client.goodbye`.
|
|
14947
16130
|
*/
|
|
14948
16131
|
disconnectTransport() {
|
|
14949
|
-
this.client.disconnect();
|
|
16132
|
+
this.client.disconnect({ reason: "Transport detach" });
|
|
14950
16133
|
}
|
|
14951
16134
|
/**
|
|
14952
16135
|
* Backwards-compatible alias for `disconnect()`.
|
|
@@ -15341,16 +16524,71 @@ var Granular = class _Granular {
|
|
|
15341
16524
|
};
|
|
15342
16525
|
}
|
|
15343
16526
|
/**
|
|
15344
|
-
* List
|
|
16527
|
+
* List indexed sessions using ownership filters and bounded pagination.
|
|
16528
|
+
*/
|
|
16529
|
+
async listSessions(options) {
|
|
16530
|
+
const environmentId = options.environmentId?.trim();
|
|
16531
|
+
const sandboxId = options.sandboxId?.trim();
|
|
16532
|
+
const subjectId = options.subjectId?.trim();
|
|
16533
|
+
if (!environmentId && !sandboxId && !subjectId) {
|
|
16534
|
+
throw new Error(
|
|
16535
|
+
"listSessions() requires environmentId, sandboxId, or subjectId so history cannot be scanned accidentally."
|
|
16536
|
+
);
|
|
16537
|
+
}
|
|
16538
|
+
const status = options.status || "active";
|
|
16539
|
+
const allowedStatuses = /* @__PURE__ */ new Set([
|
|
16540
|
+
"active",
|
|
16541
|
+
"closed",
|
|
16542
|
+
"expired",
|
|
16543
|
+
"failed",
|
|
16544
|
+
"timeout",
|
|
16545
|
+
"all"
|
|
16546
|
+
]);
|
|
16547
|
+
if (!allowedStatuses.has(status)) {
|
|
16548
|
+
throw new Error(`Unsupported session status: ${String(status)}`);
|
|
16549
|
+
}
|
|
16550
|
+
const limit = boundedSessionListInteger(
|
|
16551
|
+
options.limit,
|
|
16552
|
+
"limit",
|
|
16553
|
+
DEFAULT_CONVERSATION_SESSION_LIST_LIMIT,
|
|
16554
|
+
1,
|
|
16555
|
+
MAX_CONVERSATION_SESSION_LIST_LIMIT
|
|
16556
|
+
);
|
|
16557
|
+
const offset = boundedSessionListInteger(
|
|
16558
|
+
options.offset,
|
|
16559
|
+
"offset",
|
|
16560
|
+
0,
|
|
16561
|
+
0,
|
|
16562
|
+
MAX_CONVERSATION_SESSION_LIST_OFFSET
|
|
16563
|
+
);
|
|
16564
|
+
const query = new URLSearchParams({
|
|
16565
|
+
limit: String(limit),
|
|
16566
|
+
offset: String(offset)
|
|
16567
|
+
});
|
|
16568
|
+
if (environmentId) query.set("environmentId", environmentId);
|
|
16569
|
+
if (sandboxId) query.set("sandboxId", sandboxId);
|
|
16570
|
+
if (subjectId) query.set("userId", subjectId);
|
|
16571
|
+
if (options.sessionScope?.trim()) {
|
|
16572
|
+
query.set("sessionScope", options.sessionScope.trim());
|
|
16573
|
+
}
|
|
16574
|
+
if (status !== "all") query.set("status", status);
|
|
16575
|
+
const res = await this.request(
|
|
16576
|
+
`/control/sessions?${query.toString()}`
|
|
16577
|
+
);
|
|
16578
|
+
const items = Array.isArray(res.items) ? res.items : [];
|
|
16579
|
+
return items.map((row) => this.normalizeConversationSession(row));
|
|
16580
|
+
}
|
|
16581
|
+
/**
|
|
16582
|
+
* List active (open) sessions for an environment.
|
|
15345
16583
|
*/
|
|
15346
16584
|
async listOpenSessions(filters) {
|
|
15347
|
-
return this.
|
|
16585
|
+
return this.listSessions({ ...filters, status: "active" });
|
|
15348
16586
|
}
|
|
15349
16587
|
/**
|
|
15350
16588
|
* List closed sessions for an environment (conversations that have disconnected).
|
|
15351
16589
|
*/
|
|
15352
16590
|
async listClosedSessions(filters) {
|
|
15353
|
-
return this.
|
|
16591
|
+
return this.listSessions({ ...filters, status: "closed" });
|
|
15354
16592
|
}
|
|
15355
16593
|
async getUserEnvironmentState(options) {
|
|
15356
16594
|
const query = new URLSearchParams({
|
|
@@ -15385,14 +16623,6 @@ var Granular = class _Granular {
|
|
|
15385
16623
|
});
|
|
15386
16624
|
return result.readAtBySessionId || {};
|
|
15387
16625
|
}
|
|
15388
|
-
async listSessionsForEnvironment(environmentId, status) {
|
|
15389
|
-
const query = new URLSearchParams({ environmentId, status });
|
|
15390
|
-
const res = await this.request(
|
|
15391
|
-
`/control/sessions?${query.toString()}`
|
|
15392
|
-
);
|
|
15393
|
-
const items = Array.isArray(res.items) ? res.items : [];
|
|
15394
|
-
return items.map((row) => this.normalizeConversationSession(row));
|
|
15395
|
-
}
|
|
15396
16626
|
normalizeConversationSession(row) {
|
|
15397
16627
|
const sessionId = String(row.sessionId ?? row.session_id ?? "");
|
|
15398
16628
|
const environmentId = String(row.environmentId ?? row.environment_id ?? "");
|
|
@@ -15451,6 +16681,7 @@ var Granular = class _Granular {
|
|
|
15451
16681
|
*/
|
|
15452
16682
|
async createSession(options) {
|
|
15453
16683
|
const clientId = options.clientId || `client_${Date.now()}`;
|
|
16684
|
+
const sessionScope = options.sessionScope?.trim() || void 0;
|
|
15454
16685
|
await this.activateEnvironment(options.environmentId);
|
|
15455
16686
|
const envData = await this.environments.get(options.environmentId);
|
|
15456
16687
|
const environment = this.bindEnvironmentHandle(envData);
|
|
@@ -15459,6 +16690,8 @@ var Granular = class _Granular {
|
|
|
15459
16690
|
body: JSON.stringify({
|
|
15460
16691
|
environmentId: options.environmentId,
|
|
15461
16692
|
clientId,
|
|
16693
|
+
sessionScope,
|
|
16694
|
+
capabilities: sessionScope ? { sessionScope } : void 0,
|
|
15462
16695
|
initialHeap: options.initialHeap
|
|
15463
16696
|
})
|
|
15464
16697
|
});
|
|
@@ -15703,15 +16936,43 @@ var Granular = class _Granular {
|
|
|
15703
16936
|
const effects = Array.from(
|
|
15704
16937
|
this.getSandboxEffectMap(host.sandboxId).values()
|
|
15705
16938
|
).map((effect) => this.serializeEffect(effect));
|
|
15706
|
-
|
|
15707
|
-
|
|
15708
|
-
|
|
15709
|
-
|
|
15710
|
-
|
|
15711
|
-
|
|
15712
|
-
|
|
15713
|
-
|
|
15714
|
-
|
|
16939
|
+
let acceptedCount = 0;
|
|
16940
|
+
const rejected = [];
|
|
16941
|
+
try {
|
|
16942
|
+
await withTimeout(
|
|
16943
|
+
host.wsClient.call("effects.resetCatalog", {}),
|
|
16944
|
+
EFFECT_CATALOG_SYNC_TIMEOUT_MS,
|
|
16945
|
+
`effects.resetCatalog for sandbox ${host.sandboxId}`
|
|
16946
|
+
);
|
|
16947
|
+
for (const batch of chunkItems(effects, EFFECT_CATALOG_SYNC_BATCH_SIZE)) {
|
|
16948
|
+
const result = await withTimeout(
|
|
16949
|
+
host.wsClient.call("effects.addCatalog", {
|
|
16950
|
+
effects: batch
|
|
16951
|
+
}),
|
|
16952
|
+
EFFECT_CATALOG_SYNC_TIMEOUT_MS,
|
|
16953
|
+
`effects.addCatalog for sandbox ${host.sandboxId}`
|
|
16954
|
+
);
|
|
16955
|
+
acceptedCount += typeof result?.acceptedCount === "number" ? result.acceptedCount : 0;
|
|
16956
|
+
if (Array.isArray(result?.rejected)) {
|
|
16957
|
+
rejected.push(...result.rejected);
|
|
16958
|
+
}
|
|
16959
|
+
}
|
|
16960
|
+
} catch (error) {
|
|
16961
|
+
if (!isUnsupportedEffectCatalogMutation(error)) {
|
|
16962
|
+
throw error;
|
|
16963
|
+
}
|
|
16964
|
+
const result = await withTimeout(
|
|
16965
|
+
host.wsClient.call("effects.publishCatalog", {
|
|
16966
|
+
effects
|
|
16967
|
+
}),
|
|
16968
|
+
EFFECT_CATALOG_SYNC_TIMEOUT_MS,
|
|
16969
|
+
`effects.publishCatalog for sandbox ${host.sandboxId}`
|
|
16970
|
+
);
|
|
16971
|
+
acceptedCount = typeof result?.acceptedCount === "number" ? result.acceptedCount : 0;
|
|
16972
|
+
if (Array.isArray(result?.rejected)) {
|
|
16973
|
+
rejected.push(...result.rejected);
|
|
16974
|
+
}
|
|
16975
|
+
}
|
|
15715
16976
|
if (acceptedCount === 0 && rejected.length > 0) {
|
|
15716
16977
|
const detail = rejected.map(
|
|
15717
16978
|
(entry) => `${entry.name || "unknown"}: ${entry.reason || "rejected"}`
|
|
@@ -16943,6 +18204,7 @@ var HARNESS_V3_FRONTEND_ACTIONS_MODULE = "@granular/actions/frontend";
|
|
|
16943
18204
|
var HARNESS_V3_CSV_MODULE = "@granular/utils/csv";
|
|
16944
18205
|
var HARNESS_V3_XLSX_MODULE = "@granular/utils/xlsx";
|
|
16945
18206
|
var LEGACY_SANDBOX_TOOLS_MODULE_PATTERN = "\\.\\/sandbox-tools(?:\\.js)?";
|
|
18207
|
+
var HARNESS_V3_RUNTIME_MODULE_PATTERN = "@granular/(?:agent|session|domain(?:/[A-Za-z_$][\\w$]*)?|actions/(?:backend|frontend)|utils/(?:csv|xlsx))";
|
|
16946
18208
|
function hasNamedModuleImport(source, moduleName, name) {
|
|
16947
18209
|
const escapedModule = moduleName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
16948
18210
|
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -17001,6 +18263,15 @@ function reviewGeneratedJobCode(code, _options = {}) {
|
|
|
17001
18263
|
message: "Generated code must use static top-level ESM imports from the Harness v3 runtime modules. Do not use dynamic import(...)."
|
|
17002
18264
|
});
|
|
17003
18265
|
}
|
|
18266
|
+
if (new RegExp(
|
|
18267
|
+
`import\\s+\\*\\s+as\\s+[A-Za-z_$][\\w$]*\\s+from\\s*['"]${HARNESS_V3_RUNTIME_MODULE_PATTERN}['"]`
|
|
18268
|
+
).test(normalized)) {
|
|
18269
|
+
issues.push({
|
|
18270
|
+
code: "runtime_namespace_import",
|
|
18271
|
+
severity: "error",
|
|
18272
|
+
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"`.'
|
|
18273
|
+
});
|
|
18274
|
+
}
|
|
17004
18275
|
if (/\bprocess\.exit\s*\(/.test(normalized)) {
|
|
17005
18276
|
issues.push({
|
|
17006
18277
|
code: "process_exit",
|
|
@@ -18025,6 +19296,31 @@ function buildGranularAgentHeapBlock(heapSummary) {
|
|
|
18025
19296
|
entries: {}
|
|
18026
19297
|
});
|
|
18027
19298
|
}
|
|
19299
|
+
function buildGranularAgentManualActionMemorySummary(input) {
|
|
19300
|
+
const maxItems = Math.max(1, Math.min(12, input.maxItems ?? 8));
|
|
19301
|
+
const suggestions = (input.suggestions || []).filter((suggestion) => suggestion?.actionKey).slice(0, maxItems).map((suggestion) => ({
|
|
19302
|
+
actionKey: suggestion.actionKey,
|
|
19303
|
+
label: suggestion.label || null,
|
|
19304
|
+
targetClassName: suggestion.targetClassName || null,
|
|
19305
|
+
count: typeof suggestion.count === "number" && Number.isFinite(suggestion.count) ? suggestion.count : null,
|
|
19306
|
+
subjectCount: typeof suggestion.subjectCount === "number" && Number.isFinite(suggestion.subjectCount) ? suggestion.subjectCount : null,
|
|
19307
|
+
successCount: typeof suggestion.successCount === "number" && Number.isFinite(suggestion.successCount) ? suggestion.successCount : null,
|
|
19308
|
+
failureCount: typeof suggestion.failureCount === "number" && Number.isFinite(suggestion.failureCount) ? suggestion.failureCount : null,
|
|
19309
|
+
lastOccurredAt: typeof suggestion.lastOccurredAt === "number" && Number.isFinite(suggestion.lastOccurredAt) ? suggestion.lastOccurredAt : null,
|
|
19310
|
+
sampleTargetIds: Array.isArray(suggestion.sampleTargetIds) ? suggestion.sampleTargetIds.filter(
|
|
19311
|
+
(id) => typeof id === "string" && id.trim().length > 0
|
|
19312
|
+
).slice(0, 6) : []
|
|
19313
|
+
}));
|
|
19314
|
+
return [
|
|
19315
|
+
renderConstBlock("manualActionMemory", {
|
|
19316
|
+
suggestions
|
|
19317
|
+
}),
|
|
19318
|
+
"Use manualActionMemory only as behavioral context for likely next actions. Ground the current target and validate permissions before creating or running prepared actions."
|
|
19319
|
+
].join("\n");
|
|
19320
|
+
}
|
|
19321
|
+
function buildGranularAgentManualActionBlock(manualActionSummary) {
|
|
19322
|
+
return manualActionSummary?.trim() || buildGranularAgentManualActionMemorySummary({ suggestions: [] });
|
|
19323
|
+
}
|
|
18028
19324
|
function projectSessionFileSummary(liveDoc) {
|
|
18029
19325
|
const files = asRecord4(liveDoc?.files);
|
|
18030
19326
|
const byId = asRecord4(files?.byId) || {};
|
|
@@ -18056,8 +19352,12 @@ function buildGranularAgentFileBlock(fileSummary) {
|
|
|
18056
19352
|
function extractRuntimeContractExports(domainBlock) {
|
|
18057
19353
|
const classes = /* @__PURE__ */ new Set();
|
|
18058
19354
|
const actions = /* @__PURE__ */ new Set();
|
|
18059
|
-
const
|
|
18060
|
-
for (const match of domainBlock.matchAll(
|
|
19355
|
+
const classConstPattern = /export\s+declare\s+const\s+([A-Za-z_$][\w$]*)\s*:\s*EntityClass\b/g;
|
|
19356
|
+
for (const match of domainBlock.matchAll(classConstPattern)) {
|
|
19357
|
+
classes.add(match[1]);
|
|
19358
|
+
}
|
|
19359
|
+
const classDeclPattern = /export\s+declare\s+class\s+([A-Za-z_$][\w$]*)\b/g;
|
|
19360
|
+
for (const match of domainBlock.matchAll(classDeclPattern)) {
|
|
18061
19361
|
classes.add(match[1]);
|
|
18062
19362
|
}
|
|
18063
19363
|
const actionPattern = /export\s+declare\s+function\s+([A-Za-z_$][\w$]*)/g;
|
|
@@ -18562,6 +19862,9 @@ function buildGranularAgentSystemPrompt(input) {
|
|
|
18562
19862
|
});
|
|
18563
19863
|
const referentBlock = buildGranularAgentReferentBlock(input.referentSummary);
|
|
18564
19864
|
const loopBlock = buildGranularAgentLoopBlock(input.loopSummary);
|
|
19865
|
+
const manualActionBlock = buildGranularAgentManualActionBlock(
|
|
19866
|
+
input.manualActionSummary
|
|
19867
|
+
);
|
|
18565
19868
|
const knownFactsBlock = renderConstBlock(
|
|
18566
19869
|
"knownFacts",
|
|
18567
19870
|
buildKnownFactsFromCheckpoint(input.checkpoint)
|
|
@@ -18575,16 +19878,16 @@ function buildGranularAgentSystemPrompt(input) {
|
|
|
18575
19878
|
- \`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.
|
|
18576
19879
|
- 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.
|
|
18577
19880
|
- 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.
|
|
18578
|
-
- When \`replyToUser(...)\` mentions a grounded record that should remain clickable/referable, wrap only the visible record label in a self-closing inline reference tag
|
|
18579
|
-
- Treat \`showObjects(...)\` as the UI display call for user-visible records,
|
|
18580
|
-
-
|
|
18581
|
-
-
|
|
18582
|
-
-
|
|
18583
|
-
-
|
|
19881
|
+
- 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.
|
|
19882
|
+
- 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"] })\`.
|
|
19883
|
+
- \`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(...)\`.
|
|
19884
|
+
- 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.
|
|
19885
|
+
- 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()\`.
|
|
19886
|
+
- 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.
|
|
19887
|
+
- 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.
|
|
18584
19888
|
- 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.
|
|
18585
19889
|
- 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.
|
|
18586
|
-
-
|
|
18587
|
-
- 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.
|
|
19890
|
+
- 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.
|
|
18588
19891
|
- 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\`.
|
|
18589
19892
|
- \`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.
|
|
18590
19893
|
- 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.
|
|
@@ -18595,7 +19898,7 @@ function buildGranularAgentSystemPrompt(input) {
|
|
|
18595
19898
|
- When using code, assistant text must be empty or one brief summary.
|
|
18596
19899
|
- Code must be plain runnable JavaScript with top-level await.
|
|
18597
19900
|
- Use [Runtime Imports] as the authoritative module map. Import only listed module exports.
|
|
18598
|
-
- 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.
|
|
19901
|
+
- 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.
|
|
18599
19902
|
- 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.
|
|
18600
19903
|
- 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.
|
|
18601
19904
|
- 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\`.
|
|
@@ -18658,6 +19961,7 @@ ${workflowRules}
|
|
|
18658
19961
|
High-priority execution rules:
|
|
18659
19962
|
- 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.
|
|
18660
19963
|
- 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.
|
|
19964
|
+
- 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.
|
|
18661
19965
|
- 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.
|
|
18662
19966
|
- 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.
|
|
18663
19967
|
- 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.
|
|
@@ -18701,7 +20005,7 @@ Intent resolution:
|
|
|
18701
20005
|
- 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.
|
|
18702
20006
|
- 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.
|
|
18703
20007
|
- 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.
|
|
18704
|
-
- 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
|
|
20008
|
+
- 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.
|
|
18705
20009
|
- 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.
|
|
18706
20010
|
- Never call \`.get({ path: "" })\`; an empty path is not a saved reference.
|
|
18707
20011
|
- 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.
|
|
@@ -18828,20 +20132,10 @@ Ask the user when:
|
|
|
18828
20132
|
- the target is unique but the requested action is unclear
|
|
18829
20133
|
|
|
18830
20134
|
Relationship filters:
|
|
18831
|
-
-
|
|
18832
|
-
-
|
|
18833
|
-
-
|
|
18834
|
-
-
|
|
18835
|
-
- 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.
|
|
18836
|
-
- 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.
|
|
18837
|
-
- Use \`some\` only when the generated TypeScript type says \`ManyRelationFilter\`.
|
|
18838
|
-
- 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.
|
|
18839
|
-
- Use \`{ relationship: { id: "record_id" } }\` or \`{ relationship: { path: "class_record_id" } }\` when matching a known related record.
|
|
18840
|
-
- 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\`.
|
|
18841
|
-
- 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.
|
|
18842
|
-
- Use \`{ relationship: { is: { field: { equal_to: value } } } }\` only for nested field filters. Never put \`id\` or \`path\` inside \`is\`.
|
|
18843
|
-
- 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.
|
|
18844
|
-
- Do not pass a full record instance into a filter; if you already fetched a record, filter by its id or path instead.
|
|
20135
|
+
- Use the generated filter type as the authority: \`OneRelationFilter\` supports \`id\`, \`path\`, \`is\`, \`null\`, \`not_null\`; \`ManyRelationFilter\` supports those plus \`some\`.
|
|
20136
|
+
- Use \`id\` or \`path\` for a known related record; use \`is\` or \`some\` only for nested target-field filters.
|
|
20137
|
+
- 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.
|
|
20138
|
+
- Never pass a full record instance into a filter. Use its id/path or a declared relationship getter.
|
|
18845
20139
|
${domainSections.docs ? `
|
|
18846
20140
|
Domain notes:
|
|
18847
20141
|
${domainSections.docs}
|
|
@@ -18852,6 +20146,24 @@ ${actionIndex}
|
|
|
18852
20146
|
- 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.
|
|
18853
20147
|
- 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(...)\`.
|
|
18854
20148
|
- Actions listed under "Class-level" are class/static methods. Call them on the imported class, e.g. \`await Item.action_name(...)\`.
|
|
20149
|
+
- 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.
|
|
20150
|
+
- For pure field-collection requests, target the class-level entry state handle; for submit/review requests, target the nearest requested later state.
|
|
20151
|
+
- Choose the nearest target state that matches the user's words. Do not aim at a later state just because it is reachable.
|
|
20152
|
+
- 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\`.
|
|
20153
|
+
- 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.
|
|
20154
|
+
- 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.
|
|
20155
|
+
- 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()\`.
|
|
20156
|
+
- 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.
|
|
20157
|
+
- 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.
|
|
20158
|
+
- 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.
|
|
20159
|
+
- 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.
|
|
20160
|
+
- 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.
|
|
20161
|
+
- 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.
|
|
20162
|
+
- 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.
|
|
20163
|
+
- 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.
|
|
20164
|
+
- 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.
|
|
20165
|
+
- 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.
|
|
20166
|
+
- Use \`await prepared.show()\` or \`await actions.show(prepared)\` only to display an already-created prepared action again.
|
|
18855
20167
|
- 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.
|
|
18856
20168
|
- Never call a record-level action as \`Class.action_name(...)\`; that method will not exist.
|
|
18857
20169
|
- 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.
|
|
@@ -18882,6 +20194,8 @@ ${loopBlock}
|
|
|
18882
20194
|
|
|
18883
20195
|
${knownFactsBlock}
|
|
18884
20196
|
|
|
20197
|
+
${manualActionBlock}
|
|
20198
|
+
|
|
18885
20199
|
[Request]
|
|
18886
20200
|
${input.request?.trim() || "Use the latest user message in the conversation."}`;
|
|
18887
20201
|
}
|
|
@@ -19075,8 +20389,9 @@ function buildContinuationInstructionFromTemplate(resultPreview, options) {
|
|
|
19075
20389
|
}
|
|
19076
20390
|
|
|
19077
20391
|
// src/openai-usage.ts
|
|
19078
|
-
var
|
|
19079
|
-
var
|
|
20392
|
+
var OPENAI_GPT_5_4_PRICING_SOURCE_URL = "https://developers.openai.com/api/docs/models/gpt-5.4/";
|
|
20393
|
+
var OPENAI_GPT_5_6_PRICING_SOURCE_URL = "https://developers.openai.com/api/docs/pricing";
|
|
20394
|
+
var OPENAI_LONG_CONTEXT_THRESHOLD_TOKENS = 272e3;
|
|
19080
20395
|
var OPENAI_MODEL_PRICING_USD_PER_MILLION = {
|
|
19081
20396
|
"gpt-5.4": {
|
|
19082
20397
|
provider: "openai",
|
|
@@ -19085,8 +20400,26 @@ var OPENAI_MODEL_PRICING_USD_PER_MILLION = {
|
|
|
19085
20400
|
inputUsdPerMillion: 2.5,
|
|
19086
20401
|
cachedInputUsdPerMillion: 0.25,
|
|
19087
20402
|
outputUsdPerMillion: 15,
|
|
19088
|
-
sourceUrl:
|
|
19089
|
-
effectiveDate:
|
|
20403
|
+
sourceUrl: OPENAI_GPT_5_4_PRICING_SOURCE_URL,
|
|
20404
|
+
effectiveDate: "2026-05-19"
|
|
20405
|
+
},
|
|
20406
|
+
"gpt-5.6-luna": {
|
|
20407
|
+
provider: "openai",
|
|
20408
|
+
model: "gpt-5.6-luna",
|
|
20409
|
+
currency: "USD",
|
|
20410
|
+
inputUsdPerMillion: 1,
|
|
20411
|
+
cachedInputUsdPerMillion: 0.1,
|
|
20412
|
+
cacheWriteUsdPerMillion: 1.25,
|
|
20413
|
+
outputUsdPerMillion: 6,
|
|
20414
|
+
sourceUrl: OPENAI_GPT_5_6_PRICING_SOURCE_URL,
|
|
20415
|
+
effectiveDate: "2026-07-11",
|
|
20416
|
+
longContextThresholdTokens: OPENAI_LONG_CONTEXT_THRESHOLD_TOKENS,
|
|
20417
|
+
longContextPricing: {
|
|
20418
|
+
inputUsdPerMillion: 2,
|
|
20419
|
+
cachedInputUsdPerMillion: 0.2,
|
|
20420
|
+
cacheWriteUsdPerMillion: 2.5,
|
|
20421
|
+
outputUsdPerMillion: 9
|
|
20422
|
+
}
|
|
19090
20423
|
}
|
|
19091
20424
|
};
|
|
19092
20425
|
function asRecord5(value) {
|
|
@@ -19099,8 +20432,18 @@ function numberField(record, key) {
|
|
|
19099
20432
|
function microsPerMillion(usdPerMillion) {
|
|
19100
20433
|
return Math.round(usdPerMillion * 1e6);
|
|
19101
20434
|
}
|
|
19102
|
-
function getOpenAIModelPricing(model) {
|
|
19103
|
-
|
|
20435
|
+
function getOpenAIModelPricing(model, inputTokens = 0) {
|
|
20436
|
+
const pricing = OPENAI_MODEL_PRICING_USD_PER_MILLION[model];
|
|
20437
|
+
if (!pricing) return null;
|
|
20438
|
+
const threshold = pricing.longContextThresholdTokens ?? null;
|
|
20439
|
+
if (pricing.longContextPricing && typeof threshold === "number" && inputTokens > threshold) {
|
|
20440
|
+
return {
|
|
20441
|
+
...pricing,
|
|
20442
|
+
...pricing.longContextPricing,
|
|
20443
|
+
contextTier: "long"
|
|
20444
|
+
};
|
|
20445
|
+
}
|
|
20446
|
+
return { ...pricing, contextTier: "short" };
|
|
19104
20447
|
}
|
|
19105
20448
|
function normalizeOpenAIUsage(rawUsage) {
|
|
19106
20449
|
const usage = asRecord5(rawUsage);
|
|
@@ -19108,6 +20451,7 @@ function normalizeOpenAIUsage(rawUsage) {
|
|
|
19108
20451
|
return {
|
|
19109
20452
|
inputTokens: 0,
|
|
19110
20453
|
cachedInputTokens: 0,
|
|
20454
|
+
cacheWriteTokens: 0,
|
|
19111
20455
|
uncachedInputTokens: 0,
|
|
19112
20456
|
outputTokens: 0,
|
|
19113
20457
|
reasoningTokens: 0,
|
|
@@ -19123,20 +20467,28 @@ function normalizeOpenAIUsage(rawUsage) {
|
|
|
19123
20467
|
inputTokens,
|
|
19124
20468
|
numberField(inputDetails, "cached_tokens") || numberField(inputDetails, "cached_input_tokens")
|
|
19125
20469
|
);
|
|
20470
|
+
const cacheWriteTokens = Math.min(
|
|
20471
|
+
Math.max(inputTokens - cachedInputTokens, 0),
|
|
20472
|
+
numberField(inputDetails, "cache_write_tokens")
|
|
20473
|
+
);
|
|
19126
20474
|
const reasoningTokens = numberField(outputDetails, "reasoning_tokens") || numberField(outputDetails, "reasoning_output_tokens");
|
|
19127
20475
|
return {
|
|
19128
20476
|
inputTokens,
|
|
19129
20477
|
cachedInputTokens,
|
|
19130
|
-
|
|
20478
|
+
cacheWriteTokens,
|
|
20479
|
+
uncachedInputTokens: Math.max(
|
|
20480
|
+
inputTokens - cachedInputTokens - cacheWriteTokens,
|
|
20481
|
+
0
|
|
20482
|
+
),
|
|
19131
20483
|
outputTokens,
|
|
19132
20484
|
reasoningTokens,
|
|
19133
20485
|
totalTokens
|
|
19134
20486
|
};
|
|
19135
20487
|
}
|
|
19136
20488
|
function calculateOpenAITokenSpend(model, rawUsage) {
|
|
19137
|
-
const pricing = getOpenAIModelPricing(model);
|
|
19138
|
-
if (!pricing) return null;
|
|
19139
20489
|
const usage = normalizeOpenAIUsage(rawUsage);
|
|
20490
|
+
const pricing = getOpenAIModelPricing(model, usage.inputTokens);
|
|
20491
|
+
if (!pricing) return null;
|
|
19140
20492
|
const inputPricePerMillionMicros = microsPerMillion(
|
|
19141
20493
|
pricing.inputUsdPerMillion
|
|
19142
20494
|
);
|
|
@@ -19146,14 +20498,19 @@ function calculateOpenAITokenSpend(model, rawUsage) {
|
|
|
19146
20498
|
const outputPricePerMillionMicros = microsPerMillion(
|
|
19147
20499
|
pricing.outputUsdPerMillion
|
|
19148
20500
|
);
|
|
20501
|
+
const cacheWritePricePerMillionMicros = typeof pricing.cacheWriteUsdPerMillion === "number" ? microsPerMillion(pricing.cacheWriteUsdPerMillion) : null;
|
|
20502
|
+
const cacheWriteCostMicros = Math.round(
|
|
20503
|
+
usage.cacheWriteTokens * (cacheWritePricePerMillionMicros ?? inputPricePerMillionMicros) / 1e6
|
|
20504
|
+
);
|
|
19149
20505
|
const amountMicros = Math.round(
|
|
19150
|
-
(usage.uncachedInputTokens * inputPricePerMillionMicros + usage.cachedInputTokens * cachedInputPricePerMillionMicros + usage.outputTokens * outputPricePerMillionMicros) / 1e6
|
|
20506
|
+
(usage.uncachedInputTokens * inputPricePerMillionMicros + usage.cachedInputTokens * cachedInputPricePerMillionMicros + usage.cacheWriteTokens * (cacheWritePricePerMillionMicros ?? inputPricePerMillionMicros) + usage.outputTokens * outputPricePerMillionMicros) / 1e6
|
|
19151
20507
|
);
|
|
19152
20508
|
return {
|
|
19153
20509
|
provider: "openai",
|
|
19154
20510
|
model,
|
|
19155
20511
|
inputTokens: usage.inputTokens,
|
|
19156
20512
|
cachedInputTokens: usage.cachedInputTokens,
|
|
20513
|
+
cacheWriteTokens: usage.cacheWriteTokens,
|
|
19157
20514
|
uncachedInputTokens: usage.uncachedInputTokens,
|
|
19158
20515
|
outputTokens: usage.outputTokens,
|
|
19159
20516
|
reasoningTokens: usage.reasoningTokens,
|
|
@@ -19162,13 +20519,17 @@ function calculateOpenAITokenSpend(model, rawUsage) {
|
|
|
19162
20519
|
currency: "USD",
|
|
19163
20520
|
inputPricePerMillionMicros,
|
|
19164
20521
|
cachedInputPricePerMillionMicros,
|
|
20522
|
+
cacheWritePricePerMillionMicros,
|
|
20523
|
+
cacheWriteCostMicros,
|
|
19165
20524
|
outputPricePerMillionMicros,
|
|
20525
|
+
pricingContextTier: pricing.contextTier || "short",
|
|
20526
|
+
longContextThresholdTokens: pricing.longContextThresholdTokens ?? null,
|
|
19166
20527
|
pricingSource: pricing.sourceUrl,
|
|
19167
20528
|
pricingEffectiveAt: pricing.effectiveDate,
|
|
19168
20529
|
usage
|
|
19169
20530
|
};
|
|
19170
20531
|
}
|
|
19171
20532
|
|
|
19172
|
-
export { Environment, EnvironmentSession, Granular, OPENAI_MODEL_PRICING_USD_PER_MILLION, OntologyHandle, Session, WSClient, buildContinuationInstruction, buildContinuationInstructionFromTemplate, buildGranularAgentCheckpointBlock, buildGranularAgentDomainBlock, buildGranularAgentFileBlock, buildGranularAgentHeapBlock, buildGranularAgentLoopBlock, buildGranularAgentReferentBlock, buildGranularAgentRuntimeImportsBlock, buildGranularAgentSessionBlock, buildGranularAgentSystemPrompt, buildGranularAgentSystemPromptFromTemplate, buildGranularAgentToolBlock, buildGranularAgentWorkflowBlock, buildOpenAISpendEventId, buildSessionTranscript, calculateOpenAITokenSpend, consumeGranularReasoningOnlyChunk, consumeGranularReasoningTraceChunk, createHarnessVerifierSnapshot, evaluateContinuation, extractPromptTokens, getCurrentClosureId, getDefaultHarnessTemplateId, getExclusivePromptTarget, getOpenAIModelPricing, hasOpenPrompt, hashHarnessTemplateValue, invokeRegisteredEffect, isLocalApiUrl, listHarnessTemplates, normalizeEffectBehaviors, normalizeOpenAIUsage, normalizePrompt, normalizePromptChoiceOption, normalizePromptText, normalizePromptType, projectConversationReferentFocus, projectConversationReferentSummary, projectHeapSummary, projectLoopSummary, projectSessionFileSummary, projectWorkflowFocus, projectWorkflowSummary, recordOpenAIUsageSpend, renderContinuationInstructionFromTemplate, renderGranularAgentSystemPromptFromTemplate, resolveApiUrl, resolveAuthTokenForApiUrl, resolveHarnessTemplate, resolveJobPresentation, resolvePromptAnswer, reviewGeneratedJobCode, scorePromptChoiceMatch, stripGranularReasoningTrace, toGranularHttpBase, validateHarnessTemplateManifest };
|
|
20533
|
+
export { Environment, EnvironmentSession, Granular, OPENAI_MODEL_PRICING_USD_PER_MILLION, OntologyHandle, Session, WSClient, buildContinuationInstruction, buildContinuationInstructionFromTemplate, buildGranularAgentCheckpointBlock, buildGranularAgentDomainBlock, buildGranularAgentFileBlock, buildGranularAgentHeapBlock, buildGranularAgentLoopBlock, buildGranularAgentManualActionBlock, buildGranularAgentManualActionMemorySummary, buildGranularAgentReferentBlock, buildGranularAgentRuntimeImportsBlock, buildGranularAgentSessionBlock, buildGranularAgentSystemPrompt, buildGranularAgentSystemPromptFromTemplate, buildGranularAgentToolBlock, buildGranularAgentWorkflowBlock, buildOpenAISpendEventId, buildSessionTranscript, calculateOpenAITokenSpend, consumeGranularReasoningOnlyChunk, consumeGranularReasoningTraceChunk, createHarnessVerifierSnapshot, evaluateContinuation, evaluateValidationRule, extractPromptTokens, getCurrentClosureId, getDefaultHarnessTemplateId, getExclusivePromptTarget, getOpenAIModelPricing, hasOpenPrompt, hashHarnessTemplateValue, invokeRegisteredEffect, isLocalApiUrl, listHarnessTemplates, normalizeEffectBehaviors, normalizeOpenAIUsage, normalizePrompt, normalizePromptChoiceOption, normalizePromptText, normalizePromptType, projectConversationReferentFocus, projectConversationReferentSummary, projectHeapSummary, projectLoopSummary, projectSessionFileSummary, projectWorkflowFocus, projectWorkflowSummary, recordOpenAIUsageSpend, renderContinuationInstructionFromTemplate, renderGranularAgentSystemPromptFromTemplate, resolveApiUrl, resolveAuthTokenForApiUrl, resolveHarnessTemplate, resolveJobPresentation, resolvePromptAnswer, reviewGeneratedJobCode, scorePromptChoiceMatch, stripGranularReasoningTrace, toGranularHttpBase, validateHarnessTemplateManifest, validationRuleFailureMessage };
|
|
19173
20534
|
//# sourceMappingURL=index.mjs.map
|
|
19174
20535
|
//# sourceMappingURL=index.mjs.map
|