@granular-software/sdk 0.4.34 → 0.4.36
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 +1 -1
- package/dist/agent-evals.d.ts +1 -1
- package/dist/agent-evals.js +216 -33
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +216 -33
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/agent-harness.js +15 -6
- package/dist/agent-harness.js.map +1 -1
- package/dist/agent-harness.mjs +15 -6
- package/dist/agent-harness.mjs.map +1 -1
- package/dist/cli/index.js +201 -27
- package/dist/{client-iw76FL_8.d.mts → client-Cq8onk2D.d.mts} +51 -1
- package/dist/{client-iw76FL_8.d.ts → client-Cq8onk2D.d.ts} +51 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +216 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +216 -33
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -12350,6 +12350,38 @@ function normalizeUser(user) {
|
|
|
12350
12350
|
permissions: Array.isArray(user.permissions) ? user.permissions : []
|
|
12351
12351
|
};
|
|
12352
12352
|
}
|
|
12353
|
+
function normalizeEnvironmentSetupSummary(setup) {
|
|
12354
|
+
if (!setup) {
|
|
12355
|
+
return null;
|
|
12356
|
+
}
|
|
12357
|
+
const queuedRecords = Number(setup.queuedRecords || 0);
|
|
12358
|
+
const processingRecords = Number(setup.processingRecords || 0);
|
|
12359
|
+
return {
|
|
12360
|
+
...setup,
|
|
12361
|
+
setupRunId: String(setup.setupRunId || ""),
|
|
12362
|
+
environmentId: String(setup.environmentId || ""),
|
|
12363
|
+
sandboxId: String(setup.sandboxId || ""),
|
|
12364
|
+
subjectId: String(setup.subjectId || ""),
|
|
12365
|
+
triggerReason: setup.triggerReason === "fresh_after_version_update" ? "fresh_after_version_update" : "new_environment",
|
|
12366
|
+
lifecycleStatus: setup.lifecycleStatus === "completed" || setup.lifecycleStatus === "failed" ? setup.lifecycleStatus : "running",
|
|
12367
|
+
stage: typeof setup.stage === "string" ? setup.stage : null,
|
|
12368
|
+
totalObjectsToImport: Number(setup.totalObjectsToImport || 0),
|
|
12369
|
+
totalImports: Number(setup.totalImports || 0),
|
|
12370
|
+
activeImports: Number(setup.activeImports || 0),
|
|
12371
|
+
totalRecords: Number(setup.totalRecords || 0),
|
|
12372
|
+
queuedRecords,
|
|
12373
|
+
processingRecords,
|
|
12374
|
+
completedRecords: Number(setup.completedRecords || 0),
|
|
12375
|
+
failedRecords: Number(setup.failedRecords || 0),
|
|
12376
|
+
canceledRecords: Number(setup.canceledRecords || 0),
|
|
12377
|
+
awaitingRecords: Number(setup.awaitingRecords || 0) || queuedRecords + processingRecords,
|
|
12378
|
+
errorMessage: typeof setup.errorMessage === "string" ? setup.errorMessage : null,
|
|
12379
|
+
startedAt: Number(setup.startedAt || Date.now()),
|
|
12380
|
+
hookCompletedAt: setup.hookCompletedAt == null ? null : Number(setup.hookCompletedAt),
|
|
12381
|
+
finishedAt: setup.finishedAt == null ? null : Number(setup.finishedAt),
|
|
12382
|
+
updatedAt: Number(setup.updatedAt || Date.now())
|
|
12383
|
+
};
|
|
12384
|
+
}
|
|
12353
12385
|
function normalizeEnvironmentData(environment) {
|
|
12354
12386
|
const buildPolicy = environment.buildPolicy || environment.tracking || (environment.tagId ? { mode: "tag", tagId: environment.tagId } : {
|
|
12355
12387
|
mode: "pinned",
|
|
@@ -12363,7 +12395,8 @@ function normalizeEnvironmentData(environment) {
|
|
|
12363
12395
|
envName: environmentName,
|
|
12364
12396
|
environment: environmentName,
|
|
12365
12397
|
buildPolicy,
|
|
12366
|
-
tracking: environment.tracking || buildPolicy
|
|
12398
|
+
tracking: environment.tracking || buildPolicy,
|
|
12399
|
+
setup: normalizeEnvironmentSetupSummary(environment.setup)
|
|
12367
12400
|
};
|
|
12368
12401
|
}
|
|
12369
12402
|
var Environment = class {
|
|
@@ -12421,6 +12454,10 @@ var Environment = class {
|
|
|
12421
12454
|
get updateState() {
|
|
12422
12455
|
return this.envData.updateState;
|
|
12423
12456
|
}
|
|
12457
|
+
/** The latest setup/import run summary for this environment, when available. */
|
|
12458
|
+
get setup() {
|
|
12459
|
+
return this.envData.setup || null;
|
|
12460
|
+
}
|
|
12424
12461
|
/** Convenience flag for whether this environment trails the current tag target */
|
|
12425
12462
|
get isOutdated() {
|
|
12426
12463
|
return this.envData.updateState === "update_available";
|
|
@@ -12441,6 +12478,9 @@ var Environment = class {
|
|
|
12441
12478
|
get runtimeBaseUrl() {
|
|
12442
12479
|
return this.getRuntimeBaseUrl();
|
|
12443
12480
|
}
|
|
12481
|
+
syncEnvironmentData(envData) {
|
|
12482
|
+
this.envData = normalizeEnvironmentData(envData);
|
|
12483
|
+
}
|
|
12444
12484
|
get sessions() {
|
|
12445
12485
|
return {
|
|
12446
12486
|
list: async (options) => this.listSessions(options?.status || "active"),
|
|
@@ -13386,7 +13426,8 @@ var Environment = class {
|
|
|
13386
13426
|
method: "POST",
|
|
13387
13427
|
body: JSON.stringify({
|
|
13388
13428
|
records,
|
|
13389
|
-
batchSize: options.batchSize
|
|
13429
|
+
batchSize: options.batchSize,
|
|
13430
|
+
setupRunId: options.setupRunId
|
|
13390
13431
|
})
|
|
13391
13432
|
}
|
|
13392
13433
|
);
|
|
@@ -13534,18 +13575,12 @@ var EnvironmentSession = class extends Session {
|
|
|
13534
13575
|
}
|
|
13535
13576
|
get messages() {
|
|
13536
13577
|
return {
|
|
13537
|
-
list: (options = {}) => this.sessionDataRequest(
|
|
13538
|
-
"/messages",
|
|
13539
|
-
options
|
|
13540
|
-
)
|
|
13578
|
+
list: (options = {}) => this.sessionDataRequest("/messages", options)
|
|
13541
13579
|
};
|
|
13542
13580
|
}
|
|
13543
13581
|
get timeline() {
|
|
13544
13582
|
return {
|
|
13545
|
-
list: (options = {}) => this.sessionDataRequest(
|
|
13546
|
-
"/timeline",
|
|
13547
|
-
options
|
|
13548
|
-
)
|
|
13583
|
+
list: (options = {}) => this.sessionDataRequest("/timeline", options)
|
|
13549
13584
|
};
|
|
13550
13585
|
}
|
|
13551
13586
|
get jobs() {
|
|
@@ -13562,10 +13597,7 @@ var EnvironmentSession = class extends Session {
|
|
|
13562
13597
|
get heap() {
|
|
13563
13598
|
return {
|
|
13564
13599
|
entries: {
|
|
13565
|
-
list: (options = {}) => this.sessionDataRequest(
|
|
13566
|
-
"/heap/entries",
|
|
13567
|
-
options
|
|
13568
|
-
),
|
|
13600
|
+
list: (options = {}) => this.sessionDataRequest("/heap/entries", options),
|
|
13569
13601
|
get: (path) => this.sessionDataRequest(
|
|
13570
13602
|
`/heap/entries/${encodeURIComponent(path)}`
|
|
13571
13603
|
)
|
|
@@ -13609,10 +13641,7 @@ var EnvironmentSession = class extends Session {
|
|
|
13609
13641
|
const heap = normalizeHeapSnapshot({
|
|
13610
13642
|
entriesByPath: Object.fromEntries(
|
|
13611
13643
|
entries.map((entry) => {
|
|
13612
|
-
return entry?.path ? [
|
|
13613
|
-
entry.path,
|
|
13614
|
-
entry
|
|
13615
|
-
] : null;
|
|
13644
|
+
return entry?.path ? [entry.path, entry] : null;
|
|
13616
13645
|
}).filter(
|
|
13617
13646
|
(entry) => Boolean(entry)
|
|
13618
13647
|
)
|
|
@@ -13778,6 +13807,15 @@ var OntologyHandle = class {
|
|
|
13778
13807
|
disconnect: async () => this.granular.disconnectEffects(this.ontologyNameOrId)
|
|
13779
13808
|
};
|
|
13780
13809
|
}
|
|
13810
|
+
get importer() {
|
|
13811
|
+
return {
|
|
13812
|
+
onEnvironmentCreate: (handler) => this.granular.registerEnvironmentImporter(
|
|
13813
|
+
this.ontologyNameOrId,
|
|
13814
|
+
handler
|
|
13815
|
+
),
|
|
13816
|
+
clear: () => this.granular.clearEnvironmentImporter(this.ontologyNameOrId)
|
|
13817
|
+
};
|
|
13818
|
+
}
|
|
13781
13819
|
};
|
|
13782
13820
|
var Granular = class _Granular {
|
|
13783
13821
|
apiKey;
|
|
@@ -13794,6 +13832,10 @@ var Granular = class _Granular {
|
|
|
13794
13832
|
sandboxEffectHosts = /* @__PURE__ */ new Map();
|
|
13795
13833
|
/** In-flight host connection promises to avoid duplicate concurrent connects */
|
|
13796
13834
|
sandboxEffectHostPromises = /* @__PURE__ */ new Map();
|
|
13835
|
+
/** Ontology-bound environment importer hooks keyed by the caller's ontology identifier. */
|
|
13836
|
+
ontologyImporters = /* @__PURE__ */ new Map();
|
|
13837
|
+
/** Resolved importer hooks keyed by sandboxId for fast lookups during openEnvironment(). */
|
|
13838
|
+
sandboxImporters = /* @__PURE__ */ new Map();
|
|
13797
13839
|
/**
|
|
13798
13840
|
* Create a new Granular client
|
|
13799
13841
|
* @param options - Client configuration
|
|
@@ -13819,6 +13861,32 @@ var Granular = class _Granular {
|
|
|
13819
13861
|
ontology(ontologyNameOrId) {
|
|
13820
13862
|
return new OntologyHandle(this, ontologyNameOrId);
|
|
13821
13863
|
}
|
|
13864
|
+
registerEnvironmentImporter(ontologyNameOrId, handler) {
|
|
13865
|
+
this.ontologyImporters.set(ontologyNameOrId, handler);
|
|
13866
|
+
if (ontologyNameOrId.startsWith("sbx_")) {
|
|
13867
|
+
this.sandboxImporters.set(ontologyNameOrId, {
|
|
13868
|
+
handler,
|
|
13869
|
+
sourceOntology: ontologyNameOrId
|
|
13870
|
+
});
|
|
13871
|
+
return;
|
|
13872
|
+
}
|
|
13873
|
+
for (const [sandboxId, importer] of this.sandboxImporters.entries()) {
|
|
13874
|
+
if (importer.sourceOntology === ontologyNameOrId) {
|
|
13875
|
+
this.sandboxImporters.set(sandboxId, {
|
|
13876
|
+
handler,
|
|
13877
|
+
sourceOntology: ontologyNameOrId
|
|
13878
|
+
});
|
|
13879
|
+
}
|
|
13880
|
+
}
|
|
13881
|
+
}
|
|
13882
|
+
clearEnvironmentImporter(ontologyNameOrId) {
|
|
13883
|
+
this.ontologyImporters.delete(ontologyNameOrId);
|
|
13884
|
+
for (const [sandboxId, importer] of this.sandboxImporters.entries()) {
|
|
13885
|
+
if (importer.sourceOntology === ontologyNameOrId) {
|
|
13886
|
+
this.sandboxImporters.delete(sandboxId);
|
|
13887
|
+
}
|
|
13888
|
+
}
|
|
13889
|
+
}
|
|
13822
13890
|
/**
|
|
13823
13891
|
* Records/upserts a user and prepares them for sandbox connections
|
|
13824
13892
|
*
|
|
@@ -13935,11 +14003,13 @@ var Granular = class _Granular {
|
|
|
13935
14003
|
* ```
|
|
13936
14004
|
*/
|
|
13937
14005
|
async openEnvironment(options) {
|
|
13938
|
-
const
|
|
14006
|
+
const resolved = await this.resolveOpenEnvironmentData(
|
|
13939
14007
|
options,
|
|
13940
14008
|
"openEnvironment"
|
|
13941
14009
|
);
|
|
13942
|
-
|
|
14010
|
+
const environment = this.bindEnvironmentHandle(resolved.environment);
|
|
14011
|
+
await this.maybeRunEnvironmentImporter(resolved, environment);
|
|
14012
|
+
return environment;
|
|
13943
14013
|
}
|
|
13944
14014
|
/**
|
|
13945
14015
|
* Deprecated compatibility alias for `openEnvironment()`.
|
|
@@ -14026,7 +14096,12 @@ var Granular = class _Granular {
|
|
|
14026
14096
|
)
|
|
14027
14097
|
);
|
|
14028
14098
|
if (currentMatches.length > 0) {
|
|
14029
|
-
return
|
|
14099
|
+
return {
|
|
14100
|
+
environment: currentMatches[0],
|
|
14101
|
+
requestedOntology: ontology,
|
|
14102
|
+
sandboxId: sandbox.sandboxId,
|
|
14103
|
+
subjectId: user.granularId
|
|
14104
|
+
};
|
|
14030
14105
|
}
|
|
14031
14106
|
const outdatedMatches = this.sortEnvironmentsByRecency(
|
|
14032
14107
|
userEnvironments.filter(
|
|
@@ -14034,14 +14109,25 @@ var Granular = class _Granular {
|
|
|
14034
14109
|
)
|
|
14035
14110
|
);
|
|
14036
14111
|
if (outdatedMatches.length > 0 && options.createFreshIfOutdated !== true) {
|
|
14037
|
-
return
|
|
14112
|
+
return {
|
|
14113
|
+
environment: outdatedMatches[0],
|
|
14114
|
+
requestedOntology: ontology,
|
|
14115
|
+
sandboxId: sandbox.sandboxId,
|
|
14116
|
+
subjectId: user.granularId
|
|
14117
|
+
};
|
|
14038
14118
|
}
|
|
14039
|
-
return
|
|
14119
|
+
return {
|
|
14120
|
+
environment: await this.environments.create(sandbox.sandboxId, {
|
|
14121
|
+
subjectId: user.granularId,
|
|
14122
|
+
environment: this.buildManagedEnvironmentName(tagName, targetVersionId),
|
|
14123
|
+
tagId: tag.tagId,
|
|
14124
|
+
permissionProfileId: null
|
|
14125
|
+
}),
|
|
14126
|
+
requestedOntology: ontology,
|
|
14127
|
+
sandboxId: sandbox.sandboxId,
|
|
14040
14128
|
subjectId: user.granularId,
|
|
14041
|
-
|
|
14042
|
-
|
|
14043
|
-
permissionProfileId: null
|
|
14044
|
-
});
|
|
14129
|
+
setupTriggerReason: outdatedMatches.length > 0 ? "fresh_after_version_update" : "new_environment"
|
|
14130
|
+
};
|
|
14045
14131
|
}
|
|
14046
14132
|
/**
|
|
14047
14133
|
* List active (open) sessions for an environment — each session is one agent conversation thread.
|
|
@@ -14158,6 +14244,94 @@ var Granular = class _Granular {
|
|
|
14158
14244
|
});
|
|
14159
14245
|
return this.connectSession({ sessionId, clientId: options?.clientId });
|
|
14160
14246
|
}
|
|
14247
|
+
resolveEnvironmentImporter(requestedOntology, sandboxId) {
|
|
14248
|
+
const sandboxImporter = this.sandboxImporters.get(sandboxId);
|
|
14249
|
+
if (sandboxImporter) {
|
|
14250
|
+
const sourceImporter = this.ontologyImporters.get(
|
|
14251
|
+
sandboxImporter.sourceOntology
|
|
14252
|
+
);
|
|
14253
|
+
if (sourceImporter === sandboxImporter.handler) {
|
|
14254
|
+
return sandboxImporter.handler;
|
|
14255
|
+
}
|
|
14256
|
+
this.sandboxImporters.delete(sandboxId);
|
|
14257
|
+
}
|
|
14258
|
+
const ontologyImporter = this.ontologyImporters.get(requestedOntology);
|
|
14259
|
+
if (!ontologyImporter) {
|
|
14260
|
+
return void 0;
|
|
14261
|
+
}
|
|
14262
|
+
this.sandboxImporters.set(sandboxId, {
|
|
14263
|
+
handler: ontologyImporter,
|
|
14264
|
+
sourceOntology: requestedOntology
|
|
14265
|
+
});
|
|
14266
|
+
return ontologyImporter;
|
|
14267
|
+
}
|
|
14268
|
+
async maybeRunEnvironmentImporter(resolved, environment) {
|
|
14269
|
+
if (!resolved.setupTriggerReason) {
|
|
14270
|
+
return;
|
|
14271
|
+
}
|
|
14272
|
+
const importer = this.resolveEnvironmentImporter(
|
|
14273
|
+
resolved.requestedOntology,
|
|
14274
|
+
resolved.sandboxId
|
|
14275
|
+
);
|
|
14276
|
+
if (!importer) {
|
|
14277
|
+
return;
|
|
14278
|
+
}
|
|
14279
|
+
const setupRun = await this.request(
|
|
14280
|
+
`/control/environments/${environment.environmentId}/setup-runs`,
|
|
14281
|
+
{
|
|
14282
|
+
method: "POST",
|
|
14283
|
+
body: JSON.stringify({
|
|
14284
|
+
triggerReason: resolved.setupTriggerReason
|
|
14285
|
+
})
|
|
14286
|
+
}
|
|
14287
|
+
);
|
|
14288
|
+
const setupRunId = setupRun.setupRunId;
|
|
14289
|
+
const updateSetupRun = async (patch) => {
|
|
14290
|
+
await this.request(
|
|
14291
|
+
`/control/environment-setup-runs/${setupRunId}`,
|
|
14292
|
+
{
|
|
14293
|
+
method: "PATCH",
|
|
14294
|
+
body: JSON.stringify(patch)
|
|
14295
|
+
}
|
|
14296
|
+
);
|
|
14297
|
+
};
|
|
14298
|
+
const importerContext = {
|
|
14299
|
+
environmentId: environment.environmentId,
|
|
14300
|
+
sandboxId: environment.sandboxId,
|
|
14301
|
+
subjectId: environment.subjectId,
|
|
14302
|
+
reason: resolved.setupTriggerReason,
|
|
14303
|
+
incrementTotalObjectsToImportCount: async (n) => {
|
|
14304
|
+
const safeIncrement = Math.max(0, Math.trunc(n));
|
|
14305
|
+
if (safeIncrement <= 0) {
|
|
14306
|
+
return;
|
|
14307
|
+
}
|
|
14308
|
+
await updateSetupRun({
|
|
14309
|
+
incrementTotalObjectsToImportCount: safeIncrement
|
|
14310
|
+
});
|
|
14311
|
+
},
|
|
14312
|
+
setStage: async (stage) => {
|
|
14313
|
+
await updateSetupRun({ stage });
|
|
14314
|
+
},
|
|
14315
|
+
importRecords: async (records, options) => environment.enqueueRecordImport(records, {
|
|
14316
|
+
batchSize: options?.batchSize,
|
|
14317
|
+
setupRunId
|
|
14318
|
+
})
|
|
14319
|
+
};
|
|
14320
|
+
try {
|
|
14321
|
+
await importer(importerContext);
|
|
14322
|
+
await updateSetupRun({ markHookCompleted: true });
|
|
14323
|
+
const refreshedEnvironment = await this.environments.get(
|
|
14324
|
+
environment.environmentId
|
|
14325
|
+
);
|
|
14326
|
+
environment.syncEnvironmentData(refreshedEnvironment);
|
|
14327
|
+
} catch (error) {
|
|
14328
|
+
await updateSetupRun({
|
|
14329
|
+
status: "failed",
|
|
14330
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
14331
|
+
}).catch(() => void 0);
|
|
14332
|
+
throw error;
|
|
14333
|
+
}
|
|
14334
|
+
}
|
|
14161
14335
|
bindEnvironmentHandle(envData) {
|
|
14162
14336
|
const graphqlEndpoint = `${this.httpUrl}/orchestrator/graphql`;
|
|
14163
14337
|
return new Environment(this, envData, this.apiKey, graphqlEndpoint);
|
|
@@ -15799,12 +15973,11 @@ function buildContinuationInstruction(resultPreview) {
|
|
|
15799
15973
|
"Continue the same user request using the latest structured session state.",
|
|
15800
15974
|
"Take only the minimum next step that directly helps the user.",
|
|
15801
15975
|
"Use the active tasks, decisions, prompts, and heap references as the source of truth instead of replaying old work.",
|
|
15802
|
-
"If the user names a concrete record that is not already in the heap,
|
|
15976
|
+
"If the user names a concrete record that is not already in the heap, resolve it from the graph before saying it is missing: try a broad search, then a small set of normalized/fuzzy variants or a paged scan when the domain supports it.",
|
|
15977
|
+
"If the request needs all matching records, use iterate(...) or page until hasMore is false. A single list(...) or page(...) call is only one page.",
|
|
15803
15978
|
"If this request clearly spans multiple steps and there are no active tasks yet, create 2-4 short user-visible tasks now.",
|
|
15804
15979
|
"Reuse any existing taskId and decisionId values exactly as they appear in AGENT LOOP STATE.",
|
|
15805
|
-
"
|
|
15806
|
-
"Do not ask for confirmation in plain text. Use loop.confirm(...) when approval is needed.",
|
|
15807
|
-
"Await loop.ask_user(...) and loop.confirm(...). Those helpers pause the current job and resume it after the user answers.",
|
|
15980
|
+
"When progress depends on the user's choice, missing detail, or approval, use loop.ask_user(...) or loop.confirm(...) so the job pauses and resumes through the live workflow.",
|
|
15808
15981
|
"After a resumed ask_user or confirm call, continue the same job and perform the newly authorized action when the answer is sufficient. Do not stop with placeholder text like 'I'm ready to do it next.'",
|
|
15809
15982
|
"If you ask the user a new question in this job, do not also close the loop in the same job.",
|
|
15810
15983
|
"Write the smallest straightforward code for the current step. Avoid defensive fallback branches for hypothetical states that are not currently true.",
|
|
@@ -15992,12 +16165,13 @@ ${loopBlock}
|
|
|
15992
16165
|
- Continue from the latest structured state. Treat WORKFLOW SNAPSHOT, EXECUTION CHECKPOINT, RECENT REFERENTS, SESSION HEAP, and AGENT LOOP STATE as the working memory for this request.
|
|
15993
16166
|
- Use CAPABILITY SNAPSHOT to choose the next step, then use DOMAIN REFERENCE for exact signatures and query shapes.
|
|
15994
16167
|
- Take the minimum next step that directly helps the user. Avoid duplicate work, speculative cleanup, or extra fetching that is not needed yet.
|
|
15995
|
-
- If the user names a record that is not already in the heap, fetch it from the graph instead of saying it is not in context.
|
|
15996
|
-
- Treat user-provided names as human references, not exact keys. If one strong partial match exists, use it. If several plausible matches exist, ask the user to choose.
|
|
15997
16168
|
- Use RECENT REFERENTS to resolve follow-up references across turns, such as "that invoice", "that customer", "those products", or "the other one".
|
|
16169
|
+
- Treat user-provided names, numbers, and labels as human references, not exact keys. Resolve them with code: check recent referents/heap first, then query the graph with the broadest supported \`search\` or \`filter\`, then retry with a few normalized/fuzzy/prefix variants when the first pass is empty or ambiguous. Only say a record does not exist after a reasonable lookup across the relevant class.
|
|
16170
|
+
- If one strong match exists, use it. If several plausible matches remain, use \`loop.ask_user({ type: 'choice', ... })\` with the grounded candidates instead of guessing.
|
|
15998
16171
|
- If the request has more than one reasonable interpretation, ask the user to clarify instead of guessing.
|
|
15999
16172
|
- For comparisons, rankings, selections, or summaries, first identify the rule you are using. If that rule is not clear from the user request and DOMAIN REFERENCE, ask the user before choosing anything.
|
|
16000
16173
|
- When the ranking, comparison, or selection rule is unclear, the minimum next step is the clarification itself. Do not run a placeholder query for a provisional winner before asking.
|
|
16174
|
+
- If a user request matches both a domain type/effect and a loop helper, prioritize the domain type/effect. For example, if DOMAIN REFERENCE contains a \`Task\` class and the user asks to create a task, create the domain task record; do not call \`loop.create_task(...)\` unless you are only tracking your own workflow.
|
|
16001
16175
|
- Reuse exact \`taskId\`, \`decisionId\`, and \`closureId\` values from AGENT LOOP STATE. Never invent or rewrite them.
|
|
16002
16176
|
- If the request is ambiguous or clearly multi-step, create 2-4 short user-visible tasks and keep them updated as the workflow advances.
|
|
16003
16177
|
- Use \`loop.ask_user({ type: 'choice', options: [...] })\` when you have a short, grounded shortlist the user can choose from. Otherwise use \`type: 'input'\`.
|
|
@@ -16008,6 +16182,14 @@ ${loopBlock}
|
|
|
16008
16182
|
- Use \`loop.open_decision(...)\` to persist grounded candidates, \`loop.close_decision(...)\` to resolve one, and \`loop.close_loop(...)\` when the workflow is completed, canceled, or blocked.
|
|
16009
16183
|
- If you ask a new question in the current job, do not also close the loop in that same job.
|
|
16010
16184
|
|
|
16185
|
+
\u2500\u2500\u2500 LOOP HELPER REFERENCE \u2500\u2500\u2500
|
|
16186
|
+
- \`loop.ask_user(...)\`: pause the current job for missing input; use \`type: 'choice'\` only for a short grounded shortlist.
|
|
16187
|
+
- \`loop.confirm(...)\`: pause for yes/no approval before a consequential action, then branch on the returned boolean.
|
|
16188
|
+
- \`loop.open_decision(...)\`: save explicit candidates that later jobs can revisit; each candidate needs an \`id\`.
|
|
16189
|
+
- \`loop.close_decision(...)\`: resolve an open decision with a stored \`selectedId\` and optional rationale.
|
|
16190
|
+
- \`loop.create_task(...)\`, \`loop.update_task(...)\`, \`loop.complete_task(...)\`: keep a short resumable task list for the agent's workflow; these are not domain \`Task\` records.
|
|
16191
|
+
- \`loop.close_loop(...)\`: record the workflow outcome when it is completed, canceled, or blocked.
|
|
16192
|
+
|
|
16011
16193
|
\u2500\u2500\u2500 CODE RULES \u2500\u2500\u2500
|
|
16012
16194
|
- Import from \`./sandbox-tools\`.
|
|
16013
16195
|
- If you use \`heap\`, \`loop\`, \`agent_text_message\`, or \`agent_heap_objects\`, import them explicitly from \`./sandbox-tools\`.
|
|
@@ -16017,6 +16199,7 @@ ${loopBlock}
|
|
|
16017
16199
|
- Use \`ClassName.get({ path })\` only for known graph paths when you want a direct graph fetch.
|
|
16018
16200
|
- Use \`ClassName.count()\` for totals, \`ClassName.page({ page, perPage, saveAs })\` when you need \`items\` plus \`totalCount\` or \`hasMore\`, \`ClassName.list({ page, perPage, saveAs })\` for one page of records, and \`ClassName.iterate({ perPage, maxItems })\` for large scans.
|
|
16019
16201
|
- \`perPage\` defaults to \`100\` and is capped at \`100\`.
|
|
16202
|
+
- A single \`list(...)\` or \`page(...)\` call never proves there are no more records. For "all", "every", exports, broad scans, or exhaustive searches, use \`iterate(...)\` when available or loop \`page(...)\` until \`hasMore\` is false.
|
|
16020
16203
|
- Push \`filter\`, \`search\`, and \`sort\` into graph queries instead of fetching a page and processing it locally.
|
|
16021
16204
|
- A property appearing on a record does not make it valid in \`filter\` or \`sort\`; only use fields and operators that are explicitly exposed in DOMAIN REFERENCE.
|
|
16022
16205
|
- Choose \`sort.field\` verbatim from the sortable fields listed in DOMAIN REFERENCE. Do not sort by relationship names, related-record collections, counts, totals, or other derived metrics unless they are explicitly listed as sortable.
|