@granular-software/sdk 0.4.45 → 0.4.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-evals.d.mts +7 -3
- package/dist/agent-evals.d.ts +7 -3
- package/dist/agent-evals.js +423 -37
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +423 -37
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/agent-harness.d.mts +1 -0
- package/dist/agent-harness.d.ts +1 -0
- package/dist/agent-harness.js +87 -11
- package/dist/agent-harness.js.map +1 -1
- package/dist/agent-harness.mjs +87 -11
- package/dist/agent-harness.mjs.map +1 -1
- package/dist/cli/index.js +114 -18
- package/dist/{client-BZ8NuQ_e.d.ts → client-ButG6ePW.d.ts} +7 -1
- package/dist/{client-CBQFvuKf.d.mts → client-C1UqPDwe.d.mts} +7 -1
- package/dist/index.d.mts +3 -4
- package/dist/index.d.ts +3 -4
- package/dist/index.js +201 -29
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +201 -29
- package/dist/index.mjs.map +1 -1
- package/dist/{spend-tAz2a16I.d.mts → spend-D2Vy3N1D.d.mts} +113 -4
- package/dist/{spend-tAz2a16I.d.ts → spend-D2Vy3N1D.d.ts} +113 -4
- package/dist/spend.d.mts +1 -2
- package/dist/spend.d.ts +1 -2
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -4022,6 +4022,9 @@ var MAX_TIMER_DELAY_MS = 2147483647;
|
|
|
4022
4022
|
var DEBUG_WS = process.env.GRANULAR_DEBUG_WS === "1";
|
|
4023
4023
|
var DEFAULT_RPC_TIMEOUT_MS = 3e4;
|
|
4024
4024
|
var DOMAIN_PACKAGE_RPC_TIMEOUT_MS = 12e4;
|
|
4025
|
+
var EFFECT_CONTROL_RPC_TIMEOUT_MS = 12e4;
|
|
4026
|
+
var DEFAULT_RECONNECT_DELAY_MS = 3e3;
|
|
4027
|
+
var DEFAULT_MAX_RECONNECT_ATTEMPTS = 5;
|
|
4025
4028
|
function debugWs(...args) {
|
|
4026
4029
|
if (DEBUG_WS) {
|
|
4027
4030
|
console.log(...args);
|
|
@@ -4032,6 +4035,10 @@ function rpcTimeoutMsForMethod(method) {
|
|
|
4032
4035
|
case "domain.fetchPackagePart":
|
|
4033
4036
|
case "domain.getSummary":
|
|
4034
4037
|
return DOMAIN_PACKAGE_RPC_TIMEOUT_MS;
|
|
4038
|
+
case "client.heartbeat":
|
|
4039
|
+
case "effects.publishCatalog":
|
|
4040
|
+
case "effects.refresh":
|
|
4041
|
+
return EFFECT_CONTROL_RPC_TIMEOUT_MS;
|
|
4035
4042
|
default:
|
|
4036
4043
|
return DEFAULT_RPC_TIMEOUT_MS;
|
|
4037
4044
|
}
|
|
@@ -4051,6 +4058,7 @@ var WSClient = class {
|
|
|
4051
4058
|
reconnectTimer = null;
|
|
4052
4059
|
tokenRefreshTimer = null;
|
|
4053
4060
|
isExplicitlyDisconnected = false;
|
|
4061
|
+
reconnectAttempts = 0;
|
|
4054
4062
|
options;
|
|
4055
4063
|
constructor(options) {
|
|
4056
4064
|
this.options = options;
|
|
@@ -4205,6 +4213,7 @@ var WSClient = class {
|
|
|
4205
4213
|
clearTimeout(this.reconnectTimer);
|
|
4206
4214
|
this.reconnectTimer = null;
|
|
4207
4215
|
}
|
|
4216
|
+
this.reconnectAttempts = 0;
|
|
4208
4217
|
this.emit("open", {});
|
|
4209
4218
|
resolve();
|
|
4210
4219
|
});
|
|
@@ -4236,6 +4245,7 @@ var WSClient = class {
|
|
|
4236
4245
|
clearTimeout(this.reconnectTimer);
|
|
4237
4246
|
this.reconnectTimer = null;
|
|
4238
4247
|
}
|
|
4248
|
+
this.reconnectAttempts = 0;
|
|
4239
4249
|
this.emit("open", {});
|
|
4240
4250
|
resolve();
|
|
4241
4251
|
};
|
|
@@ -4295,7 +4305,8 @@ var WSClient = class {
|
|
|
4295
4305
|
return new Error(`WebSocket disconnected${suffix}`);
|
|
4296
4306
|
}
|
|
4297
4307
|
handleDisconnect(close = {}) {
|
|
4298
|
-
const
|
|
4308
|
+
const baseReconnectDelayMs = typeof this.options.reconnectDelayMs === "number" && Number.isFinite(this.options.reconnectDelayMs) && this.options.reconnectDelayMs > 0 ? this.options.reconnectDelayMs : DEFAULT_RECONNECT_DELAY_MS;
|
|
4309
|
+
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;
|
|
4299
4310
|
const unexpected = !this.isExplicitlyDisconnected;
|
|
4300
4311
|
const info = {
|
|
4301
4312
|
code: close.code,
|
|
@@ -4315,6 +4326,30 @@ var WSClient = class {
|
|
|
4315
4326
|
const disconnectError = this.buildDisconnectError(info);
|
|
4316
4327
|
this.rejectPending(disconnectError);
|
|
4317
4328
|
this.emit("disconnect", info);
|
|
4329
|
+
if (this.reconnectAttempts >= maxReconnectAttempts) {
|
|
4330
|
+
const reconnectInfo = {
|
|
4331
|
+
error: `WebSocket reconnect attempts exhausted after ${maxReconnectAttempts} attempt(s).`,
|
|
4332
|
+
sessionId: this.sessionId,
|
|
4333
|
+
timestamp: Date.now()
|
|
4334
|
+
};
|
|
4335
|
+
this.emit("reconnect_error", reconnectInfo);
|
|
4336
|
+
if (this.options.onReconnectError) {
|
|
4337
|
+
try {
|
|
4338
|
+
this.options.onReconnectError(reconnectInfo);
|
|
4339
|
+
} catch (callbackError) {
|
|
4340
|
+
console.error(
|
|
4341
|
+
"[Granular] onReconnectError callback failed:",
|
|
4342
|
+
callbackError
|
|
4343
|
+
);
|
|
4344
|
+
}
|
|
4345
|
+
}
|
|
4346
|
+
return;
|
|
4347
|
+
}
|
|
4348
|
+
this.reconnectAttempts += 1;
|
|
4349
|
+
const reconnectDelayMs = Math.min(
|
|
4350
|
+
3e4,
|
|
4351
|
+
baseReconnectDelayMs * 2 ** Math.max(0, this.reconnectAttempts - 1)
|
|
4352
|
+
);
|
|
4318
4353
|
info.reconnectScheduled = true;
|
|
4319
4354
|
info.reconnectDelayMs = reconnectDelayMs;
|
|
4320
4355
|
if (this.options.onUnexpectedClose) {
|
|
@@ -4769,6 +4804,9 @@ var Session = class {
|
|
|
4769
4804
|
promptCache = /* @__PURE__ */ new Map();
|
|
4770
4805
|
/** Prompt ids locally answered before the document sync catches up. */
|
|
4771
4806
|
hiddenPromptIds = /* @__PURE__ */ new Set();
|
|
4807
|
+
domainPackagePartCache = /* @__PURE__ */ new Map();
|
|
4808
|
+
domainPackagePartPromises = /* @__PURE__ */ new Map();
|
|
4809
|
+
domainPackageFetchQueue = Promise.resolve();
|
|
4772
4810
|
constructor(client, clientId, options = {}) {
|
|
4773
4811
|
this.client = client;
|
|
4774
4812
|
this.clientId = clientId || `client_${Date.now()}`;
|
|
@@ -4976,12 +5014,18 @@ var Session = class {
|
|
|
4976
5014
|
const resolvedAnswer = resolvePromptAnswer(prompt, answer);
|
|
4977
5015
|
this.promptCache.delete(promptId);
|
|
4978
5016
|
this.hiddenPromptIds.add(promptId);
|
|
5017
|
+
this.emit("prompt", { id: promptId, status: "answered" });
|
|
4979
5018
|
try {
|
|
4980
|
-
await this.client.call("prompt.answer", {
|
|
5019
|
+
const response = await this.client.call("prompt.answer", {
|
|
4981
5020
|
promptId,
|
|
4982
5021
|
answer: resolvedAnswer,
|
|
4983
5022
|
value: resolvedAnswer
|
|
4984
5023
|
});
|
|
5024
|
+
if (response && typeof response === "object" && "ok" in response && response.ok === false) {
|
|
5025
|
+
const rejected = response;
|
|
5026
|
+
const errorMessage = typeof rejected.error === "string" ? rejected.error : "Prompt answer was rejected.";
|
|
5027
|
+
throw new Error(errorMessage);
|
|
5028
|
+
}
|
|
4985
5029
|
} catch (error) {
|
|
4986
5030
|
this.hiddenPromptIds.delete(promptId);
|
|
4987
5031
|
if (prompt) {
|
|
@@ -5209,11 +5253,33 @@ var Session = class {
|
|
|
5209
5253
|
* Fetch a domain package part from the backend (no fallback).
|
|
5210
5254
|
*/
|
|
5211
5255
|
async fetchDomainPart(part) {
|
|
5212
|
-
const
|
|
5213
|
-
|
|
5214
|
-
|
|
5256
|
+
const cached = this.domainPackagePartCache.get(part);
|
|
5257
|
+
if (cached !== void 0) {
|
|
5258
|
+
return cached;
|
|
5259
|
+
}
|
|
5260
|
+
const inFlight = this.domainPackagePartPromises.get(part);
|
|
5261
|
+
if (inFlight) {
|
|
5262
|
+
return inFlight;
|
|
5263
|
+
}
|
|
5264
|
+
const fetchPromise = this.domainPackageFetchQueue.then(async () => {
|
|
5265
|
+
const result = await this.client.call("domain.fetchPackagePart", {
|
|
5266
|
+
moduleSpecifier: "@sandbox/domain",
|
|
5267
|
+
part
|
|
5268
|
+
});
|
|
5269
|
+
const content = result?.content ?? "";
|
|
5270
|
+
this.domainPackagePartCache.set(part, content);
|
|
5271
|
+
return content;
|
|
5215
5272
|
});
|
|
5216
|
-
|
|
5273
|
+
this.domainPackagePartPromises.set(part, fetchPromise);
|
|
5274
|
+
this.domainPackageFetchQueue = fetchPromise.then(
|
|
5275
|
+
() => void 0,
|
|
5276
|
+
() => void 0
|
|
5277
|
+
);
|
|
5278
|
+
try {
|
|
5279
|
+
return await fetchPromise;
|
|
5280
|
+
} finally {
|
|
5281
|
+
this.domainPackagePartPromises.delete(part);
|
|
5282
|
+
}
|
|
5217
5283
|
}
|
|
5218
5284
|
/**
|
|
5219
5285
|
* Get TypeScript class declarations for the current domain (for LLM/code gen).
|
|
@@ -5463,7 +5529,10 @@ import { ${allImports} } from "./sandbox-tools";
|
|
|
5463
5529
|
const emitPrompt = (payload) => {
|
|
5464
5530
|
const prompt = normalizePrompt(payload);
|
|
5465
5531
|
if (!prompt) return;
|
|
5466
|
-
this.hiddenPromptIds.
|
|
5532
|
+
if (this.hiddenPromptIds.has(prompt.id)) {
|
|
5533
|
+
this.emit("prompt", { ...prompt, status: "answered" });
|
|
5534
|
+
return;
|
|
5535
|
+
}
|
|
5467
5536
|
this.promptCache.set(prompt.id, prompt);
|
|
5468
5537
|
this.emit("prompt", prompt);
|
|
5469
5538
|
};
|
|
@@ -13074,6 +13143,7 @@ var LOCAL_CONTROL_REQUEST_RETRY_DELAY_MS = 500;
|
|
|
13074
13143
|
var SESSION_DATA_REQUEST_RETRY_COUNT = 4;
|
|
13075
13144
|
var SESSION_DATA_REQUEST_RETRY_DELAY_MS = 500;
|
|
13076
13145
|
var EFFECT_HOST_CONNECT_TIMEOUT_MS = 15e3;
|
|
13146
|
+
var SESSION_CONNECT_TIMEOUT_MS = 15e3;
|
|
13077
13147
|
function filenameFromUploadBody(body) {
|
|
13078
13148
|
const maybe = body;
|
|
13079
13149
|
return typeof maybe.name === "string" && maybe.name.trim() ? maybe.name.trim() : null;
|
|
@@ -13093,7 +13163,7 @@ function bodyInitFromSessionFileUpload(body) {
|
|
|
13093
13163
|
}
|
|
13094
13164
|
return body;
|
|
13095
13165
|
}
|
|
13096
|
-
var EFFECT_CATALOG_SYNC_TIMEOUT_MS =
|
|
13166
|
+
var EFFECT_CATALOG_SYNC_TIMEOUT_MS = 12e4;
|
|
13097
13167
|
var EFFECT_CATALOG_SYNC_RETRY_COUNT = 3;
|
|
13098
13168
|
var EFFECT_CATALOG_SYNC_RETRY_DELAY_MS = 1e3;
|
|
13099
13169
|
function planRecordObjectsChunks(records, batchSize) {
|
|
@@ -15049,7 +15119,14 @@ var Granular = class _Granular {
|
|
|
15049
15119
|
return tag;
|
|
15050
15120
|
}
|
|
15051
15121
|
buildManagedEnvironmentName(tag, versionId) {
|
|
15052
|
-
return `__sdk__${tag}__${versionId}`;
|
|
15122
|
+
return `__sdk__${tag}__${versionId}__pinned`;
|
|
15123
|
+
}
|
|
15124
|
+
isManagedEnvironmentName(environment, tagName) {
|
|
15125
|
+
const name = environment.environment || environment.envName || "";
|
|
15126
|
+
return name.startsWith(`__sdk__${tagName}__`);
|
|
15127
|
+
}
|
|
15128
|
+
isPinnedToVersion(environment, versionId) {
|
|
15129
|
+
return environment.buildPolicy.mode === "pinned" && (environment.versionId === versionId || environment.buildPolicy.versionId === versionId || environment.buildPolicy.buildId === versionId);
|
|
15053
15130
|
}
|
|
15054
15131
|
matchesTagTrackedEnvironment(environment, tagName, tagId) {
|
|
15055
15132
|
const environmentTagName = environment.tag?.name || environment.buildPolicy.tagName || null;
|
|
@@ -15110,7 +15187,7 @@ var Granular = class _Granular {
|
|
|
15110
15187
|
);
|
|
15111
15188
|
const currentMatches = this.sortEnvironmentsByRecency(
|
|
15112
15189
|
userEnvironments.filter(
|
|
15113
|
-
(environment) => this.matchesTagTrackedEnvironment(environment, tagName, tag.tagId) && environment.versionId === targetVersionId
|
|
15190
|
+
(environment) => this.matchesTagTrackedEnvironment(environment, tagName, tag.tagId) && environment.versionId === targetVersionId && (!this.isManagedEnvironmentName(environment, tagName) || this.isPinnedToVersion(environment, targetVersionId))
|
|
15114
15191
|
)
|
|
15115
15192
|
);
|
|
15116
15193
|
if (currentMatches.length > 0) {
|
|
@@ -15139,6 +15216,7 @@ var Granular = class _Granular {
|
|
|
15139
15216
|
subjectId: user.granularId,
|
|
15140
15217
|
environment: this.buildManagedEnvironmentName(tagName, targetVersionId),
|
|
15141
15218
|
tagId: tag.tagId,
|
|
15219
|
+
versionId: targetVersionId,
|
|
15142
15220
|
permissionProfileId: null
|
|
15143
15221
|
}),
|
|
15144
15222
|
requestedOntology: ontology,
|
|
@@ -15184,6 +15262,7 @@ var Granular = class _Granular {
|
|
|
15184
15262
|
row.summaryUpdatedAt ?? row.summary_updated_at
|
|
15185
15263
|
) : null,
|
|
15186
15264
|
subjectId: row.subjectId != null ? String(row.subjectId) : null,
|
|
15265
|
+
sessionScope: row.sessionScope != null || row.session_scope != null ? String(row.sessionScope ?? row.session_scope) : null,
|
|
15187
15266
|
jobCount: typeof row.jobCount === "number" ? row.jobCount : void 0,
|
|
15188
15267
|
toolCallCount: typeof row.toolCallCount === "number" ? row.toolCallCount : void 0
|
|
15189
15268
|
};
|
|
@@ -15391,7 +15470,11 @@ var Granular = class _Granular {
|
|
|
15391
15470
|
onUnexpectedClose: this.onUnexpectedClose,
|
|
15392
15471
|
onReconnectError: this.onReconnectError
|
|
15393
15472
|
});
|
|
15394
|
-
await
|
|
15473
|
+
await withTimeout(
|
|
15474
|
+
client.connect(),
|
|
15475
|
+
SESSION_CONNECT_TIMEOUT_MS,
|
|
15476
|
+
`session WebSocket connect for ${session.sessionId}`
|
|
15477
|
+
);
|
|
15395
15478
|
const environmentSession = new EnvironmentSession(
|
|
15396
15479
|
client,
|
|
15397
15480
|
environment,
|
|
@@ -15562,12 +15645,24 @@ var Granular = class _Granular {
|
|
|
15562
15645
|
host.heartbeatInFlight = false;
|
|
15563
15646
|
}
|
|
15564
15647
|
async synchronizeEffectHost(host) {
|
|
15565
|
-
|
|
15566
|
-
|
|
15567
|
-
|
|
15568
|
-
|
|
15569
|
-
|
|
15570
|
-
|
|
15648
|
+
if (host.syncPromise) {
|
|
15649
|
+
return host.syncPromise;
|
|
15650
|
+
}
|
|
15651
|
+
host.syncPromise = (async () => {
|
|
15652
|
+
await host.wsClient.call("client.hello", {
|
|
15653
|
+
clientId: host.clientId,
|
|
15654
|
+
protocolVersion: "2.0"
|
|
15655
|
+
});
|
|
15656
|
+
await this.publishSandboxEffectCatalog(host);
|
|
15657
|
+
this.startEffectHostHeartbeat(host);
|
|
15658
|
+
})();
|
|
15659
|
+
try {
|
|
15660
|
+
await host.syncPromise;
|
|
15661
|
+
} finally {
|
|
15662
|
+
if (host.syncPromise) {
|
|
15663
|
+
host.syncPromise = null;
|
|
15664
|
+
}
|
|
15665
|
+
}
|
|
15571
15666
|
}
|
|
15572
15667
|
async ensureSandboxEffectHost(sandboxId) {
|
|
15573
15668
|
const existing = this.sandboxEffectHosts.get(sandboxId);
|
|
@@ -15603,7 +15698,8 @@ var Granular = class _Granular {
|
|
|
15603
15698
|
wsClient,
|
|
15604
15699
|
heartbeatTimer: null,
|
|
15605
15700
|
heartbeatInFlight: false,
|
|
15606
|
-
recovering: false
|
|
15701
|
+
recovering: false,
|
|
15702
|
+
syncPromise: null
|
|
15607
15703
|
};
|
|
15608
15704
|
wsClient.registerRpcHandler("effect.invoke", async (params) => {
|
|
15609
15705
|
const request = params;
|
|
@@ -17710,7 +17806,8 @@ function buildGranularAgentSessionBlock(sessionContext) {
|
|
|
17710
17806
|
runtimeId: sessionContext?.sandboxId || null,
|
|
17711
17807
|
environmentId: sessionContext?.environmentId || null,
|
|
17712
17808
|
userName: sessionContext?.userName || null,
|
|
17713
|
-
domainRevision: sessionContext?.domainRevision || null
|
|
17809
|
+
domainRevision: sessionContext?.domainRevision || null,
|
|
17810
|
+
uiContext: sessionContext?.uiContext || null
|
|
17714
17811
|
});
|
|
17715
17812
|
}
|
|
17716
17813
|
function buildGranularAgentHeapBlock(heapSummary) {
|
|
@@ -17957,7 +18054,7 @@ function buildGranularAgentToolBlock(tools, capabilityOverrides) {
|
|
|
17957
18054
|
const rightScope = `${right.className || "global"}:${right.static ? "static" : "instance"}`;
|
|
17958
18055
|
return leftScope.localeCompare(rightScope) || left.name.localeCompare(right.name);
|
|
17959
18056
|
});
|
|
17960
|
-
const
|
|
18057
|
+
const availableActions = normalizedTools.filter((tool) => tool.ready !== false).map((tool) => {
|
|
17961
18058
|
const scope = tool.className ? `${tool.static ? "class" : "record"}:${tool.className}` : "global";
|
|
17962
18059
|
return {
|
|
17963
18060
|
name: tool.name,
|
|
@@ -17968,7 +18065,8 @@ function buildGranularAgentToolBlock(tools, capabilityOverrides) {
|
|
|
17968
18065
|
const capabilities = {
|
|
17969
18066
|
executeCode: resolvedCapabilities.executeCode,
|
|
17970
18067
|
readEntities: resolvedCapabilities.readEntities,
|
|
17971
|
-
|
|
18068
|
+
availableActions,
|
|
18069
|
+
writeActions: availableActions,
|
|
17972
18070
|
workflowHelpers: resolvedCapabilities.workflowHelpers,
|
|
17973
18071
|
savedData: resolvedCapabilities.savedData,
|
|
17974
18072
|
showRecords: resolvedCapabilities.showRecords
|
|
@@ -17982,7 +18080,7 @@ function buildGranularAgentActionIndex(tools) {
|
|
|
17982
18080
|
return leftScope.localeCompare(rightScope) || left.name.localeCompare(right.name);
|
|
17983
18081
|
});
|
|
17984
18082
|
if (normalizedTools.length === 0) {
|
|
17985
|
-
return "No
|
|
18083
|
+
return "No executable actions are available.";
|
|
17986
18084
|
}
|
|
17987
18085
|
const globalTools = normalizedTools.filter((tool) => !tool.className);
|
|
17988
18086
|
const staticTools = normalizedTools.filter(
|
|
@@ -18076,6 +18174,76 @@ function splitDomainDocumentation(domainDocumentation) {
|
|
|
18076
18174
|
}
|
|
18077
18175
|
return { types: normalized, docs: "" };
|
|
18078
18176
|
}
|
|
18177
|
+
var DOMAIN_HELPER_FUNCTION_NAMES = /* @__PURE__ */ new Set([
|
|
18178
|
+
"agent_heap_objects",
|
|
18179
|
+
"agent_message",
|
|
18180
|
+
"agent_text_message"
|
|
18181
|
+
]);
|
|
18182
|
+
function inferGlobalActionToolsFromDomainTypes(domainTypes) {
|
|
18183
|
+
const inferred = [];
|
|
18184
|
+
const seen = /* @__PURE__ */ new Set();
|
|
18185
|
+
const declarationPattern = /(?:export\s+)?declare\s+function\s+([A-Za-z_$][\w$]*)\s*\(/g;
|
|
18186
|
+
let match;
|
|
18187
|
+
while (match = declarationPattern.exec(domainTypes)) {
|
|
18188
|
+
const name = match[1];
|
|
18189
|
+
if (!name || DOMAIN_HELPER_FUNCTION_NAMES.has(name) || seen.has(name)) {
|
|
18190
|
+
continue;
|
|
18191
|
+
}
|
|
18192
|
+
seen.add(name);
|
|
18193
|
+
inferred.push({
|
|
18194
|
+
name,
|
|
18195
|
+
description: "Executable global action declared by the domain runtime."
|
|
18196
|
+
});
|
|
18197
|
+
}
|
|
18198
|
+
const actionLinePattern = /^-\s*([A-Za-z_$][\w$]*)\s+\(global\):\s*(.+)$/gm;
|
|
18199
|
+
while (match = actionLinePattern.exec(domainTypes)) {
|
|
18200
|
+
const name = match[1];
|
|
18201
|
+
if (!name || DOMAIN_HELPER_FUNCTION_NAMES.has(name) || seen.has(name)) {
|
|
18202
|
+
continue;
|
|
18203
|
+
}
|
|
18204
|
+
seen.add(name);
|
|
18205
|
+
inferred.push({
|
|
18206
|
+
name,
|
|
18207
|
+
description: match[2]?.trim() || "Executable global action declared by the domain runtime."
|
|
18208
|
+
});
|
|
18209
|
+
}
|
|
18210
|
+
const scopedActionLinePattern = /^-\s*([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)\s+\((record|class)\):\s*(.+)$/gm;
|
|
18211
|
+
while (match = scopedActionLinePattern.exec(domainTypes)) {
|
|
18212
|
+
const className = match[1]?.toLowerCase();
|
|
18213
|
+
const name = match[2];
|
|
18214
|
+
const scope = match[3];
|
|
18215
|
+
if (!className || !name || DOMAIN_HELPER_FUNCTION_NAMES.has(name)) {
|
|
18216
|
+
continue;
|
|
18217
|
+
}
|
|
18218
|
+
const key = `${className}:${scope}:${name}`;
|
|
18219
|
+
if (seen.has(key)) {
|
|
18220
|
+
continue;
|
|
18221
|
+
}
|
|
18222
|
+
seen.add(key);
|
|
18223
|
+
inferred.push({
|
|
18224
|
+
name,
|
|
18225
|
+
className,
|
|
18226
|
+
static: scope === "class",
|
|
18227
|
+
description: match[4]?.trim() || "Executable action declared by the domain runtime."
|
|
18228
|
+
});
|
|
18229
|
+
}
|
|
18230
|
+
return inferred;
|
|
18231
|
+
}
|
|
18232
|
+
function resolvePromptTools(tools, domainTypes) {
|
|
18233
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
18234
|
+
for (const tool of tools || []) {
|
|
18235
|
+
if (!tool?.name) continue;
|
|
18236
|
+
const key = `${tool.className || "global"}:${tool.static ? "static" : "instance"}:${tool.name}`;
|
|
18237
|
+
byKey.set(key, tool);
|
|
18238
|
+
}
|
|
18239
|
+
for (const tool of inferGlobalActionToolsFromDomainTypes(domainTypes)) {
|
|
18240
|
+
const key = `global:instance:${tool.name}`;
|
|
18241
|
+
if (!byKey.has(key)) {
|
|
18242
|
+
byKey.set(key, tool);
|
|
18243
|
+
}
|
|
18244
|
+
}
|
|
18245
|
+
return [...byKey.values()];
|
|
18246
|
+
}
|
|
18079
18247
|
function buildGranularAgentCheckpointBlock(checkpoint) {
|
|
18080
18248
|
if (!checkpoint) {
|
|
18081
18249
|
return renderConstBlock("previousCodeResult", null);
|
|
@@ -18148,12 +18316,13 @@ function buildGranularAgentSystemPrompt(input) {
|
|
|
18148
18316
|
const outputMode = input.outputMode || "agentMessages";
|
|
18149
18317
|
const promptCapabilities = resolvePromptCapabilities(input.capabilities);
|
|
18150
18318
|
const domainSections = splitDomainDocumentation(input.domainDocumentation);
|
|
18319
|
+
const promptTools = resolvePromptTools(input.tools, domainSections.types);
|
|
18151
18320
|
const sessionBlock = buildGranularAgentSessionBlock(input.sessionContext);
|
|
18152
18321
|
const toolBlock = buildGranularAgentToolBlock(
|
|
18153
|
-
|
|
18322
|
+
promptTools,
|
|
18154
18323
|
input.capabilities
|
|
18155
18324
|
);
|
|
18156
|
-
const actionIndex = buildGranularAgentActionIndex(
|
|
18325
|
+
const actionIndex = buildGranularAgentActionIndex(promptTools);
|
|
18157
18326
|
const domainBlock = buildGranularAgentDomainBlock(domainSections.types);
|
|
18158
18327
|
const workflowBlock = buildGranularAgentWorkflowBlock(input.workflowSummary);
|
|
18159
18328
|
const checkpointBlock = buildGranularAgentCheckpointBlock(input.checkpoint);
|
|
@@ -18181,7 +18350,7 @@ function buildGranularAgentSystemPrompt(input) {
|
|
|
18181
18350
|
- When \`agent_text_message(...)\` mentions a grounded record that should remain clickable/referable, wrap only the visible record label in a self-closing inline reference tag: \`<granular-object class="class_name" id="stable_id_or_path" label="Visible label" />\`. Use the actual class name and stable id/path from the runtime record or effect result; do not invent ids, field names, or snake/camel-case aliases that are not present in the type declarations or returned object.
|
|
18182
18351
|
- Treat \`agent_heap_objects(...)\` as the UI display call for user-visible records, not as a general storage helper. Do not wrap records under an \`items\` key.
|
|
18183
18352
|
- When records should remain reusable for follow-ups, first save the runtime record or ordered record array with \`await heap.setVar("stable_selection_name", value)\`, then display that saved selection exactly once with \`await agent_heap_objects({ variableNames: ["stable_selection_name"] })\`.
|
|
18184
|
-
- \`heap.setVar(...)\` only accepts scalar values, runtime records/sandbox instances, or arrays of runtime records/sandbox instances. Do not save plain action/effect result objects. If an action returns
|
|
18353
|
+
- \`heap.setVar(...)\` only accepts scalar values, runtime records/sandbox instances, or arrays of runtime records/sandbox instances from one class. Do not save plain action/effect result objects or arrays of JSON summaries returned by actions. If an action returns ids/paths for records that should remain referable or displayed as records, fetch the matching runtime records first with the generated class \`.get(...)\`/query API, then save/display those fetched records. If the action returned only structured summaries, answer from those summaries with \`agent_text_message(...)\`.
|
|
18185
18354
|
- Do not use \`agent_heap_objects({ entries: [...] })\` or \`agent_heap_objects({ saveAs, entries })\` as a shortcut for ordered pages, queues, search results, or ranked lists; those forms can create duplicate or poorly labelled displays. Save the selection with \`heap.setVar(...)\` and display it via \`variableNames\` instead.
|
|
18186
18355
|
- Use \`entryPaths\` only for a few already-known individual records and \`listNames\` only for a host-created list that you intentionally want to show. Do not display both an entry/list selection and a heap variable for the same records.
|
|
18187
18356
|
- When the user asks to show, list, display, open, or "show them" for records you found, call \`agent_heap_objects(...)\`; do not answer only with a count or text summary.
|
|
@@ -18231,10 +18400,11 @@ ${outputRules}` : `Code:
|
|
|
18231
18400
|
- Use choice only for 2 to 5 short grounded options.
|
|
18232
18401
|
- For record choices, set each option value to a stable scalar such as the record \`_graphPath\` or \`id\`, not a label-only value.
|
|
18233
18402
|
- After \`await loop.ask_user(...)\` returns from a choice prompt, tolerate either the option value, the option object, or a human-readable label by matching against value, id/path, label, and description before failing. If a returned label is a prefix or substring of exactly one option label, treat it as that option.
|
|
18234
|
-
- Use \`loop.confirm(...)\` for yes/no confirmation only when the user explicitly asks for
|
|
18403
|
+
- Use \`loop.confirm(...)\` for yes/no confirmation only when the user explicitly asks for a separate confirmation step, policy requires confirmation outside the action runtime, or material uncertainty remains after grounding.
|
|
18404
|
+
- If action, effect, tool, or permission metadata already marks the invoked action as confirmation-gated, do not call \`loop.confirm(...)\` before invoking it. Ground the target and input, then call the action once; the runtime action policy will surface the confirmation prompt and resume the same invocation after approval.
|
|
18235
18405
|
- Do not add a generic yes/no confirmation after the user has already made a grounded choice, unless one of those confirmation conditions still applies.
|
|
18236
|
-
- Do not add confirmation only because an allowed mutation is visible to other people, customer-facing, or consequential. If the user clearly requested the mutation and the grounded target, action, and condition are unique, perform the mutation unless confirmation is required
|
|
18237
|
-
- A conditional request such as "if this is true, do that" is authorization to perform the requested action after you verify the condition. Once the condition, target, and action are grounded uniquely, call the action directly; do not ask "should I perform/post/send this?" unless the user, policy
|
|
18406
|
+
- Do not add confirmation only because an allowed mutation is visible to other people, customer-facing, or consequential. If the user clearly requested the mutation and the grounded target, action, and condition are unique, perform the mutation unless confirmation is required outside the action runtime or remaining material uncertainty exists.
|
|
18407
|
+
- A conditional request such as "if this is true, do that" is authorization to perform the requested action after you verify the condition. Once the condition, target, and action are grounded uniquely, call the action directly; do not ask "should I perform/post/send this?" unless the user, policy outside the action runtime, or unresolved material uncertainty requires confirmation. The visibility or impact of an allowed action is not by itself unresolved uncertainty.
|
|
18238
18408
|
- If the user explicitly asks you to stop for confirmation, natural-language text such as "please confirm" is not enough: call \`await loop.confirm(...)\` before the mutation, then perform the approved mutation in the same resumed job when it returns true.
|
|
18239
18409
|
- Reuse existing task, decision, and closure ids from [State].
|
|
18240
18410
|
- If a user request matches both a domain record/action and a workflow helper, prefer the domain capability.` : "";
|
|
@@ -18380,7 +18550,8 @@ Query policy:
|
|
|
18380
18550
|
- For operational blocker, risk, status, or "what is happening" questions, inspect the relevant record's scalar fields such as status, priority, blocker, summary, latest update/message, due date, amount, and other domain-specific descriptive fields before answering.
|
|
18381
18551
|
- For read-only readiness, risk, health, or status summaries, call any visible read-only assessment/status action on the grounded primary record before ad-hoc aggregation when such an action semantically matches the request. Use the returned fields in the reply and supplement with counts or record reads only when useful.
|
|
18382
18552
|
- Do not hide required visible read-only assessment/status actions inside broad try/catch blocks. The runtime action surface should show that the assessment action ran.
|
|
18383
|
-
- Treat action/effect results as structured values, not necessarily arrays. Before indexing, iterating, checking \`.length\`, or calling array methods, normalize the result first: use the result itself only when \`Array.isArray(result)\`; otherwise read the exact array field shown in the output schema, or a documented array field such as \`items\`, \`matches\`, \`results\`, \`records\`, \`entries\`, \`candidates\`, \`options\`, or
|
|
18553
|
+
- Treat action/effect results as structured values, not necessarily arrays. Before indexing, iterating, checking \`.length\`, or calling array methods, normalize the result first: use the result itself only when \`Array.isArray(result)\`; otherwise read the exact array field shown in the output schema, or a documented array field such as \`items\`, \`matches\`, \`results\`, \`records\`, \`entries\`, \`candidates\`, \`options\`, \`requests\`, \`vendors\`, \`transactions\`, \`approvals\`, \`receipts\`, or another domain-specific array field. If a structured result has \`count > 0\`, never conclude there are no matches until you inspect every array-valued field on that result object, especially fields named by the output schema. Never convert a non-array object result to \`[]\` before checking its documented fields.
|
|
18554
|
+
- Plain JSON objects returned by actions are not sandbox record instances, even when they contain ids, titles, labels, or status fields. Use them for reasoning and text responses. Do not pass action-returned JSON objects or arrays directly to \`heap.setVar(...)\` or \`agent_heap_objects(...)\`; fetch corresponding runtime records first when the user needs record display or follow-up references.
|
|
18384
18555
|
- When a visible search, lookup, availability, or assessment action returns candidates or matches, treat those returned records as already scoped by the action inputs unless the output schema gives reliable fields for further narrowing. When matching returned candidates to grounded records, use the output schema's actual identifier fields, including \`id\`, \`path\`, or fields ending in \`Id\`; do not assume candidates have \`_graphPath\`. Do not discard all returned candidates by re-filtering on guessed property names.
|
|
18385
18556
|
- For scheduling actions, convert relative wording into concrete ISO timestamps before mutating records.
|
|
18386
18557
|
- When a decision depends on fresh external state and a visible read-only status/lookup action exists on the grounded record, call it before deciding, mutating, or refusing based on stale stored fields.
|
|
@@ -18442,6 +18613,7 @@ ${domainSections.docs}
|
|
|
18442
18613
|
|
|
18443
18614
|
Actions:
|
|
18444
18615
|
${actionIndex}
|
|
18616
|
+
- Global actions are executable functions exported by "./sandbox-tools"; import each global action you call, e.g. \`import { some_action } from "./sandbox-tools"; await some_action(...)\`. This includes frontend actions such as opening, focusing, or navigating the host UI.
|
|
18445
18617
|
- 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(...)\`.
|
|
18446
18618
|
- Actions listed under "Class-level" are class/static methods. Call them on the imported class, e.g. \`await Item.action_name(...)\`.
|
|
18447
18619
|
- 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.
|