@granular-software/sdk 0.4.70 → 0.4.71
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 +2 -2
- package/dist/agent-evals.d.ts +2 -2
- package/dist/agent-evals.js +286 -27
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +286 -27
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/agent-harness.js +4 -0
- package/dist/agent-harness.js.map +1 -1
- package/dist/agent-harness.mjs +4 -0
- package/dist/agent-harness.mjs.map +1 -1
- package/dist/cli/index.js +282 -27
- package/dist/{client-D1gq4TO4.d.ts → client-JCISbded.d.ts} +23 -3
- package/dist/{client-qgWZ495o.d.mts → client-p5EhOwpP.d.mts} +23 -3
- package/dist/index.d.mts +67 -4
- package/dist/index.d.ts +67 -4
- package/dist/index.js +1396 -232
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1385 -233
- package/dist/index.mjs.map +1 -1
- package/dist/{spend-B-IQmp7F.d.mts → spend-lJDtuAr6.d.mts} +109 -2
- package/dist/{spend-B-IQmp7F.d.ts → spend-lJDtuAr6.d.ts} +109 -2
- package/dist/spend.d.mts +1 -1
- package/dist/spend.d.ts +1 -1
- package/package.json +1 -1
package/dist/agent-evals.mjs
CHANGED
|
@@ -5291,6 +5291,38 @@ function stableDocumentFingerprint(value) {
|
|
|
5291
5291
|
(key) => `${JSON.stringify(key)}:${stableDocumentFingerprint(record[key])}`
|
|
5292
5292
|
).join(",")}}`;
|
|
5293
5293
|
}
|
|
5294
|
+
function canonicalDocumentFingerprint(value) {
|
|
5295
|
+
const stripPresentation = (input, path2) => {
|
|
5296
|
+
if (input === null || typeof input !== "object") return input;
|
|
5297
|
+
if (input instanceof Date || input instanceof Uint8Array) return input;
|
|
5298
|
+
if (Array.isArray(input)) {
|
|
5299
|
+
return input.map((entry) => stripPresentation(entry, path2));
|
|
5300
|
+
}
|
|
5301
|
+
const record = input;
|
|
5302
|
+
const output = {};
|
|
5303
|
+
for (const [key, entry] of Object.entries(record)) {
|
|
5304
|
+
if (path2.length === 1 && path2[0] === "feed" && [
|
|
5305
|
+
"transientById",
|
|
5306
|
+
"feedbackUsageByJob",
|
|
5307
|
+
"revision",
|
|
5308
|
+
"lastTransientOrdinal"
|
|
5309
|
+
].includes(key)) {
|
|
5310
|
+
continue;
|
|
5311
|
+
}
|
|
5312
|
+
if (path2.length === 3 && path2[0] === "jobs" && path2[1] === "byId" && [
|
|
5313
|
+
"actionTrace",
|
|
5314
|
+
"actionSummary",
|
|
5315
|
+
"actionSummaryText",
|
|
5316
|
+
"lastActionAt"
|
|
5317
|
+
].includes(key)) {
|
|
5318
|
+
continue;
|
|
5319
|
+
}
|
|
5320
|
+
output[key] = stripPresentation(entry, [...path2, key]);
|
|
5321
|
+
}
|
|
5322
|
+
return output;
|
|
5323
|
+
};
|
|
5324
|
+
return stableDocumentFingerprint(stripPresentation(value, []));
|
|
5325
|
+
}
|
|
5294
5326
|
function debugWs(...args) {
|
|
5295
5327
|
if (DEBUG_WS) {
|
|
5296
5328
|
console.log(...args);
|
|
@@ -5608,6 +5640,7 @@ var WSClient = class {
|
|
|
5608
5640
|
}
|
|
5609
5641
|
this.lastInboundActivityAt = Date.now();
|
|
5610
5642
|
this.reconnectAttempts = 0;
|
|
5643
|
+
this.connectPromise = null;
|
|
5611
5644
|
this.emit("open", {});
|
|
5612
5645
|
finish();
|
|
5613
5646
|
};
|
|
@@ -5770,7 +5803,7 @@ var WSClient = class {
|
|
|
5770
5803
|
this.canonicalDocumentActivated = true;
|
|
5771
5804
|
}
|
|
5772
5805
|
}
|
|
5773
|
-
canAcceptDocumentReplacement(replacement, strictlyNewer) {
|
|
5806
|
+
canAcceptDocumentReplacement(replacement, strictlyNewer, options = {}) {
|
|
5774
5807
|
const replacementCanonical = isCanonicalSessionFeedDocument(replacement);
|
|
5775
5808
|
const currentCanonical = isCanonicalSessionFeedDocument(this.doc);
|
|
5776
5809
|
if (replacementCanonical) {
|
|
@@ -5819,7 +5852,7 @@ var WSClient = class {
|
|
|
5819
5852
|
debugWs(`[Granular DEBUG] Rejected session snapshot: ${canonicalError}`);
|
|
5820
5853
|
return false;
|
|
5821
5854
|
}
|
|
5822
|
-
if (this.canonicalDocumentActivated && incoming.epoch === current.epoch && incoming.revision === current.revision && !readSessionFeedSnapshot(this.doc).error && stableDocumentFingerprint(Automerge.toJS(replacement)) !== stableDocumentFingerprint(Automerge.toJS(this.doc))) {
|
|
5855
|
+
if (this.canonicalDocumentActivated && incoming.epoch === current.epoch && incoming.revision === current.revision && !readSessionFeedSnapshot(this.doc).error && (options.allowEqualRevisionPresentation ? canonicalDocumentFingerprint(Automerge.toJS(replacement)) : stableDocumentFingerprint(Automerge.toJS(replacement))) !== (options.allowEqualRevisionPresentation ? canonicalDocumentFingerprint(Automerge.toJS(this.doc)) : stableDocumentFingerprint(Automerge.toJS(this.doc)))) {
|
|
5823
5856
|
debugWs(
|
|
5824
5857
|
"[Granular DEBUG] Rejected divergent canonical snapshot at the accepted document version."
|
|
5825
5858
|
);
|
|
@@ -5941,6 +5974,7 @@ var WSClient = class {
|
|
|
5941
5974
|
reconnectScheduled: false
|
|
5942
5975
|
};
|
|
5943
5976
|
this.ws = null;
|
|
5977
|
+
this.connectPromise = null;
|
|
5944
5978
|
this.emit("close", info);
|
|
5945
5979
|
if (this.reconnectTimer) {
|
|
5946
5980
|
clearTimeout(this.reconnectTimer);
|
|
@@ -5978,13 +6012,89 @@ var WSClient = class {
|
|
|
5978
6012
|
);
|
|
5979
6013
|
return;
|
|
5980
6014
|
}
|
|
6015
|
+
if ("type" in message && message.type === "job_action_trace") {
|
|
6016
|
+
const update = message;
|
|
6017
|
+
if (typeof update.jobId !== "string" || update.jobId.length === 0 || !Number.isSafeInteger(update.documentEpoch) || update.documentEpoch < 0 || !Number.isSafeInteger(update.documentRevision) || update.documentRevision < 0 || !Array.isArray(update.actionTrace) || update.actionTrace.length > 128 || !update.actionTrace.every(
|
|
6018
|
+
(event) => event && typeof event === "object" && typeof event.id === "string" && typeof event.timestamp === "number" && Number.isFinite(event.timestamp)
|
|
6019
|
+
) || !Array.isArray(update.actionSummary) || update.actionSummary.length > 16 || !update.actionSummary.every((line) => typeof line === "string") || typeof update.actionSummaryText !== "string" || update.actionSummaryText.length > 8e3) {
|
|
6020
|
+
console.warn("[Granular] Ignoring invalid job_action_trace message");
|
|
6021
|
+
return;
|
|
6022
|
+
}
|
|
6023
|
+
const currentVersion = documentVersion(this.doc);
|
|
6024
|
+
if (update.documentEpoch < currentVersion.epoch || update.documentEpoch === currentVersion.epoch && update.documentRevision < currentVersion.revision) {
|
|
6025
|
+
return;
|
|
6026
|
+
}
|
|
6027
|
+
if (update.documentEpoch > currentVersion.epoch || update.documentEpoch === currentVersion.epoch && update.documentRevision > currentVersion.revision) {
|
|
6028
|
+
return;
|
|
6029
|
+
}
|
|
6030
|
+
const current = Automerge.toJS(this.doc);
|
|
6031
|
+
const job = current.jobs?.byId?.[update.jobId];
|
|
6032
|
+
if (job && typeof job === "object" && !Array.isArray(job)) {
|
|
6033
|
+
job.actionTrace = update.actionTrace;
|
|
6034
|
+
job.actionSummary = update.actionSummary;
|
|
6035
|
+
job.actionSummaryText = update.actionSummaryText;
|
|
6036
|
+
const lastEvent = update.actionTrace[update.actionTrace.length - 1];
|
|
6037
|
+
if (lastEvent && typeof lastEvent.timestamp === "number") {
|
|
6038
|
+
job.lastActionAt = lastEvent.timestamp;
|
|
6039
|
+
}
|
|
6040
|
+
this.doc = Automerge.from(current);
|
|
6041
|
+
this.emit("job_action_trace", update);
|
|
6042
|
+
this.emit("sync", this.doc);
|
|
6043
|
+
}
|
|
6044
|
+
return;
|
|
6045
|
+
}
|
|
6046
|
+
if ("type" in message && message.type === "feed_transient_patch") {
|
|
6047
|
+
const update = message;
|
|
6048
|
+
if (!Number.isSafeInteger(update.documentEpoch) || update.documentEpoch < 0 || !Number.isSafeInteger(update.documentRevision) || update.documentRevision < 0 || !Number.isSafeInteger(update.feedRevision) || update.feedRevision < 0 || !Number.isSafeInteger(update.lastTransientOrdinal) || update.lastTransientOrdinal < 0 || !Array.isArray(update.changes) || update.changes.length > 128 || !update.changes.every((change) => {
|
|
6049
|
+
if (!change || typeof change !== "object" || typeof change.transientId !== "string" || change.transientId.length === 0) {
|
|
6050
|
+
return false;
|
|
6051
|
+
}
|
|
6052
|
+
if (change.removed === true) return change.item === void 0;
|
|
6053
|
+
const item = change.item;
|
|
6054
|
+
return Boolean(
|
|
6055
|
+
item && typeof item === "object" && !Array.isArray(item) && item.id === change.transientId
|
|
6056
|
+
);
|
|
6057
|
+
})) {
|
|
6058
|
+
console.warn("[Granular] Ignoring invalid feed_transient_patch");
|
|
6059
|
+
return;
|
|
6060
|
+
}
|
|
6061
|
+
const currentVersion = documentVersion(this.doc);
|
|
6062
|
+
if (update.documentEpoch > currentVersion.epoch || update.documentEpoch === currentVersion.epoch && update.documentRevision > currentVersion.revision || update.documentEpoch < currentVersion.epoch || update.documentEpoch === currentVersion.epoch && update.documentRevision < currentVersion.revision) {
|
|
6063
|
+
return;
|
|
6064
|
+
}
|
|
6065
|
+
const current = Automerge.toJS(this.doc);
|
|
6066
|
+
const feed = current.feed;
|
|
6067
|
+
if (!feed || typeof feed !== "object" || Array.isArray(feed)) return;
|
|
6068
|
+
if (Number.isSafeInteger(feed.revision) && feed.revision > update.feedRevision) {
|
|
6069
|
+
return;
|
|
6070
|
+
}
|
|
6071
|
+
if (Number.isSafeInteger(feed.lastTransientOrdinal) && feed.lastTransientOrdinal > update.lastTransientOrdinal) {
|
|
6072
|
+
return;
|
|
6073
|
+
}
|
|
6074
|
+
if (!feed.transientById || typeof feed.transientById !== "object") {
|
|
6075
|
+
feed.transientById = {};
|
|
6076
|
+
}
|
|
6077
|
+
for (const change of update.changes) {
|
|
6078
|
+
if (change.removed === true) {
|
|
6079
|
+
delete feed.transientById[change.transientId];
|
|
6080
|
+
} else {
|
|
6081
|
+
feed.transientById[change.transientId] = change.item;
|
|
6082
|
+
}
|
|
6083
|
+
}
|
|
6084
|
+
feed.revision = update.feedRevision;
|
|
6085
|
+
feed.lastTransientOrdinal = update.lastTransientOrdinal;
|
|
6086
|
+
this.doc = Automerge.from(current);
|
|
6087
|
+
this.emit("feed_transient_patch", update);
|
|
6088
|
+
this.emit("sync", this.doc);
|
|
6089
|
+
return;
|
|
6090
|
+
}
|
|
5981
6091
|
if ("type" in message && message.type === "snapshot_reset") {
|
|
5982
6092
|
const resetMessage = message;
|
|
5983
6093
|
try {
|
|
5984
|
-
if (!Number.isSafeInteger(resetMessage.documentEpoch) || resetMessage.documentEpoch <= 0 || !Number.isSafeInteger(resetMessage.documentRevision) || resetMessage.documentRevision < 0 || !Array.isArray(resetMessage.data)) {
|
|
6094
|
+
if (!Number.isSafeInteger(resetMessage.documentEpoch) || resetMessage.documentEpoch <= 0 || !Number.isSafeInteger(resetMessage.documentRevision) || resetMessage.documentRevision < 0 || !Array.isArray(resetMessage.data) && (!resetMessage.document || typeof resetMessage.document !== "object" || Array.isArray(resetMessage.document))) {
|
|
5985
6095
|
throw new Error("snapshot_reset metadata is invalid");
|
|
5986
6096
|
}
|
|
5987
|
-
const replacement = Automerge.load(
|
|
6097
|
+
const replacement = resetMessage.document ? Automerge.from(resetMessage.document) : Automerge.load(
|
|
5988
6098
|
this.decodeDocumentBytes(
|
|
5989
6099
|
resetMessage.data,
|
|
5990
6100
|
"Automerge snapshot_reset"
|
|
@@ -5998,7 +6108,10 @@ var WSClient = class {
|
|
|
5998
6108
|
"snapshot_reset metadata does not match the saved document"
|
|
5999
6109
|
);
|
|
6000
6110
|
}
|
|
6001
|
-
|
|
6111
|
+
const isJsonReplacement = Boolean(resetMessage.document);
|
|
6112
|
+
if (!this.canAcceptDocumentReplacement(replacement, !isJsonReplacement, {
|
|
6113
|
+
allowEqualRevisionPresentation: isJsonReplacement
|
|
6114
|
+
})) {
|
|
6002
6115
|
if (this.documentRepairRequired) {
|
|
6003
6116
|
this.requireDocumentResync(
|
|
6004
6117
|
"Stale Automerge snapshot_reset during document repair",
|
|
@@ -12343,6 +12456,7 @@ var StateMachineTransitionSchema = external_exports.object({
|
|
|
12343
12456
|
to: external_exports.string().min(1),
|
|
12344
12457
|
label: external_exports.string().optional(),
|
|
12345
12458
|
description: external_exports.string().optional(),
|
|
12459
|
+
userVisible: external_exports.boolean().optional(),
|
|
12346
12460
|
action: StateTransitionActionSchema.optional(),
|
|
12347
12461
|
assignee: StateTransitionAssigneeSchema.optional(),
|
|
12348
12462
|
requirements: StateTransitionRequirementsSchema.optional(),
|
|
@@ -12427,6 +12541,7 @@ var CreatesSchema = external_exports.union([
|
|
|
12427
12541
|
idPath: external_exports.string().min(1).optional(),
|
|
12428
12542
|
pathPath: external_exports.string().min(1).optional(),
|
|
12429
12543
|
statePath: external_exports.string().min(1).optional(),
|
|
12544
|
+
completionState: external_exports.string().min(1).optional(),
|
|
12430
12545
|
classStateHandle: external_exports.boolean().optional()
|
|
12431
12546
|
}).strict()
|
|
12432
12547
|
]);
|
|
@@ -12776,6 +12891,7 @@ function normalizeCreationSummary(metamodels) {
|
|
|
12776
12891
|
...typeof raw.idPath === "string" && raw.idPath.trim() ? { idPath: raw.idPath.trim() } : {},
|
|
12777
12892
|
...typeof raw.pathPath === "string" && raw.pathPath.trim() ? { pathPath: raw.pathPath.trim() } : {},
|
|
12778
12893
|
...typeof raw.statePath === "string" && raw.statePath.trim() ? { statePath: raw.statePath.trim() } : {},
|
|
12894
|
+
...typeof raw.completionState === "string" && raw.completionState.trim() ? { completionState: raw.completionState.trim() } : {},
|
|
12779
12895
|
...typeof raw.classStateHandle === "boolean" ? { classStateHandle: raw.classStateHandle } : {}
|
|
12780
12896
|
};
|
|
12781
12897
|
}
|
|
@@ -12901,7 +13017,7 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
12901
13017
|
},
|
|
12902
13018
|
{
|
|
12903
13019
|
key: "creates",
|
|
12904
|
-
description: 'Marks a static method as an allowed creator for a class. Use `creates: "class_name"` or `{ className, idPath, pathPath, statePath, classStateHandle }`.'
|
|
13020
|
+
description: 'Marks a static method as an allowed creator for a class. Use `creates: "class_name"` or `{ className, idPath, pathPath, statePath, completionState, classStateHandle }`.'
|
|
12905
13021
|
}
|
|
12906
13022
|
]
|
|
12907
13023
|
},
|
|
@@ -13134,6 +13250,11 @@ function validateProjectionResult(declaration, projection) {
|
|
|
13134
13250
|
"projection.source.version"
|
|
13135
13251
|
);
|
|
13136
13252
|
}
|
|
13253
|
+
if (projection.source.versionScope !== void 0 && projection.source.versionScope !== "global" && projection.source.versionScope !== "target") {
|
|
13254
|
+
throw new Error(
|
|
13255
|
+
"projection.source.versionScope must be 'global' or 'target'"
|
|
13256
|
+
);
|
|
13257
|
+
}
|
|
13137
13258
|
if (!Array.isArray(projection.changes)) {
|
|
13138
13259
|
throw new Error("projection.changes must be an array");
|
|
13139
13260
|
}
|
|
@@ -14791,6 +14912,7 @@ function normalizeStateMachines(values) {
|
|
|
14791
14912
|
to: String(transition?.to?.name || ""),
|
|
14792
14913
|
label: typeof transition?.label === "string" ? transition.label : null,
|
|
14793
14914
|
description: typeof transition?.description === "string" ? transition.description : null,
|
|
14915
|
+
userVisible: typeof transition?.user_visible === "boolean" ? transition.user_visible : typeof transition?.userVisible === "boolean" ? transition.userVisible : null,
|
|
14794
14916
|
action: parseJsonRecord(transition?.action) || parseJsonRecord(transition?.action_json),
|
|
14795
14917
|
assignee: parseJsonRecord(transition?.assignee) || parseJsonRecord(transition?.assignee_json),
|
|
14796
14918
|
requirements: parseJsonRecord(transition?.requirements) || parseJsonRecord(transition?.requirements_json),
|
|
@@ -14862,6 +14984,9 @@ function transitionMetadataGraphqlArgs(transition) {
|
|
|
14862
14984
|
if (typeof transition.description === "string") {
|
|
14863
14985
|
args.push(`description: ${JSON.stringify(transition.description)}`);
|
|
14864
14986
|
}
|
|
14987
|
+
if (typeof transition.userVisible === "boolean") {
|
|
14988
|
+
args.push(`user_visible: ${transition.userVisible}`);
|
|
14989
|
+
}
|
|
14865
14990
|
if (transition.action) {
|
|
14866
14991
|
args.push(
|
|
14867
14992
|
`action_json: ${JSON.stringify(JSON.stringify(transition.action))}`
|
|
@@ -15173,7 +15298,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15173
15298
|
name: String!
|
|
15174
15299
|
state_machine: StateMachine!
|
|
15175
15300
|
add_state(name: String!, is_final: Boolean, label: String, description: String): StateMachineMutation!
|
|
15176
|
-
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, outcomes_json: String): StateMachineMutation!
|
|
15301
|
+
add_transition(name: String!, from: String!, to: String!, label: String, description: String, user_visible: Boolean, action_json: String, assignee_json: String, requirements_json: String, permission_json: String, risk: String, expected_outcome_json: String, outcomes_json: String): StateMachineMutation!
|
|
15177
15302
|
activate_transition(name: String!): StateMachineMutation!
|
|
15178
15303
|
}
|
|
15179
15304
|
|
|
@@ -15231,6 +15356,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15231
15356
|
to: StateMachineState!
|
|
15232
15357
|
label: String
|
|
15233
15358
|
description: String
|
|
15359
|
+
user_visible: Boolean
|
|
15234
15360
|
action_json: String
|
|
15235
15361
|
assignee_json: String
|
|
15236
15362
|
requirements_json: String
|
|
@@ -15312,6 +15438,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15312
15438
|
to,
|
|
15313
15439
|
label,
|
|
15314
15440
|
description,
|
|
15441
|
+
user_visible,
|
|
15315
15442
|
action_json,
|
|
15316
15443
|
assignee_json,
|
|
15317
15444
|
requirements_json,
|
|
@@ -15329,6 +15456,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15329
15456
|
{
|
|
15330
15457
|
label,
|
|
15331
15458
|
description,
|
|
15459
|
+
userVisible: user_visible,
|
|
15332
15460
|
actionJson: action_json,
|
|
15333
15461
|
assigneeJson: assignee_json,
|
|
15334
15462
|
requirementsJson: requirements_json,
|
|
@@ -15393,6 +15521,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15393
15521
|
to: (value) => value.to_state || { name: value.to, is_final: false },
|
|
15394
15522
|
label: (value) => value.label || null,
|
|
15395
15523
|
description: (value) => value.description || null,
|
|
15524
|
+
user_visible: (value) => value.user_visible ?? null,
|
|
15396
15525
|
action_json: (value) => value.action_json || null,
|
|
15397
15526
|
assignee_json: (value) => value.assignee_json || null,
|
|
15398
15527
|
requirements_json: (value) => value.requirements_json || null,
|
|
@@ -15479,6 +15608,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15479
15608
|
to { name }
|
|
15480
15609
|
label
|
|
15481
15610
|
description
|
|
15611
|
+
user_visible
|
|
15482
15612
|
action_json
|
|
15483
15613
|
assignee_json
|
|
15484
15614
|
requirements_json
|
|
@@ -15788,6 +15918,104 @@ function buildEffectMetamodelMutations(toolPath, spec) {
|
|
|
15788
15918
|
);
|
|
15789
15919
|
}
|
|
15790
15920
|
|
|
15921
|
+
// src/artifact-workflow.ts
|
|
15922
|
+
var TERMINAL_WORKFLOW_STEP_STATUSES = /* @__PURE__ */ new Set([
|
|
15923
|
+
"completed",
|
|
15924
|
+
"skipped",
|
|
15925
|
+
"canceled"
|
|
15926
|
+
]);
|
|
15927
|
+
function isArtifactWorkflowNodeTerminal(nodeOrStatus) {
|
|
15928
|
+
const status = typeof nodeOrStatus === "string" ? nodeOrStatus : nodeOrStatus.status;
|
|
15929
|
+
return TERMINAL_WORKFLOW_STEP_STATUSES.has(status);
|
|
15930
|
+
}
|
|
15931
|
+
function isNavigationOnlyOperation(workflow, node) {
|
|
15932
|
+
const hasExplicitTransitionLeaf = getArtifactWorkflowNodes(workflow).some(
|
|
15933
|
+
(candidate) => candidate.childIds.length === 0 && candidate.kind === "transition"
|
|
15934
|
+
);
|
|
15935
|
+
return hasExplicitTransitionLeaf && node.childIds.length === 0 && node.kind === "operation" && // A terminal operation is redundant once authored state-path leaves are
|
|
15936
|
+
// available. While it is still delivering/synchronizing, however, it is
|
|
15937
|
+
// the only honest representation of the work still in progress.
|
|
15938
|
+
isArtifactWorkflowNodeTerminal(node);
|
|
15939
|
+
}
|
|
15940
|
+
function isArtifactWorkflowNodeNavigableLeaf(workflow, node) {
|
|
15941
|
+
return node.childIds.length === 0 && !isNavigationOnlyOperation(workflow, node);
|
|
15942
|
+
}
|
|
15943
|
+
function isArtifactWorkflowNodeActionable(node) {
|
|
15944
|
+
return node.availability === "available" && ["current", "needs_input", "working", "blocked", "failed"].includes(
|
|
15945
|
+
node.status
|
|
15946
|
+
);
|
|
15947
|
+
}
|
|
15948
|
+
function getArtifactWorkflowNodes(workflow) {
|
|
15949
|
+
const visit = (id) => {
|
|
15950
|
+
const node = workflow.nodesById[id];
|
|
15951
|
+
return node ? [node, ...node.childIds.flatMap(visit)] : [];
|
|
15952
|
+
};
|
|
15953
|
+
return workflow.rootNodeIds.flatMap(visit);
|
|
15954
|
+
}
|
|
15955
|
+
function assertArtifactWorkflow(workflow) {
|
|
15956
|
+
if (workflow.schemaVersion !== 1)
|
|
15957
|
+
throw new Error("Unsupported artifact workflow schema");
|
|
15958
|
+
if (!Number.isSafeInteger(workflow.revision) || workflow.revision < 1) {
|
|
15959
|
+
throw new Error("Artifact workflow revision must be a positive integer");
|
|
15960
|
+
}
|
|
15961
|
+
const visited = /* @__PURE__ */ new Set();
|
|
15962
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
15963
|
+
const nodes = [];
|
|
15964
|
+
const visit = (id, expectedParentId) => {
|
|
15965
|
+
const node = workflow.nodesById[id];
|
|
15966
|
+
if (!node) throw new Error(`Artifact workflow node ${id} does not exist`);
|
|
15967
|
+
if (visiting.has(id)) throw new Error("Artifact workflow contains a cycle");
|
|
15968
|
+
if (visited.has(id))
|
|
15969
|
+
throw new Error("Artifact workflow contains a duplicate node reference");
|
|
15970
|
+
if (node.parentId !== expectedParentId) {
|
|
15971
|
+
throw new Error(`Artifact workflow node ${id} has an invalid parent`);
|
|
15972
|
+
}
|
|
15973
|
+
visiting.add(id);
|
|
15974
|
+
nodes.push(node);
|
|
15975
|
+
node.childIds.forEach((childId) => visit(childId, id));
|
|
15976
|
+
visiting.delete(id);
|
|
15977
|
+
visited.add(id);
|
|
15978
|
+
};
|
|
15979
|
+
workflow.rootNodeIds.forEach((id) => visit(id, null));
|
|
15980
|
+
if (visited.size !== Object.keys(workflow.nodesById).length) {
|
|
15981
|
+
throw new Error("Artifact workflow contains unreachable nodes");
|
|
15982
|
+
}
|
|
15983
|
+
if (workflow.currentNodeId && !workflow.nodesById[workflow.currentNodeId]) {
|
|
15984
|
+
throw new Error("Artifact workflow current node does not exist");
|
|
15985
|
+
}
|
|
15986
|
+
const currentNode = workflow.currentNodeId ? workflow.nodesById[workflow.currentNodeId] : null;
|
|
15987
|
+
if (currentNode && currentNode.childIds.length > 0) {
|
|
15988
|
+
throw new Error("Artifact workflow current node must be actionable leaf");
|
|
15989
|
+
}
|
|
15990
|
+
const actionable = nodes.filter(
|
|
15991
|
+
(node) => isArtifactWorkflowNodeNavigableLeaf(workflow, node) && isArtifactWorkflowNodeActionable(node)
|
|
15992
|
+
);
|
|
15993
|
+
if (actionable.length > 1)
|
|
15994
|
+
throw new Error("Artifact workflow has multiple current nodes");
|
|
15995
|
+
if (actionable.length === 1 && actionable[0].id !== workflow.currentNodeId) {
|
|
15996
|
+
throw new Error("Artifact workflow current node does not own its action");
|
|
15997
|
+
}
|
|
15998
|
+
if (workflow.currentNodeId && !actionable.length && !isArtifactWorkflowNodeTerminal(currentNode)) {
|
|
15999
|
+
throw new Error("Artifact workflow current node is not actionable");
|
|
16000
|
+
}
|
|
16001
|
+
}
|
|
16002
|
+
function requireArtifactWorkflow(artifact) {
|
|
16003
|
+
if (!artifact.workflow) {
|
|
16004
|
+
throw new Error(
|
|
16005
|
+
`Artifact ${artifact.artifactId} has no canonical workflow`
|
|
16006
|
+
);
|
|
16007
|
+
}
|
|
16008
|
+
assertArtifactWorkflow(artifact.workflow);
|
|
16009
|
+
if (artifact.workflow.artifactId !== artifact.artifactId) {
|
|
16010
|
+
throw new Error("Artifact workflow belongs to a different artifact");
|
|
16011
|
+
}
|
|
16012
|
+
return artifact;
|
|
16013
|
+
}
|
|
16014
|
+
function requireArtifactWorkflowResult(result) {
|
|
16015
|
+
requireArtifactWorkflow(result.artifact);
|
|
16016
|
+
return result;
|
|
16017
|
+
}
|
|
16018
|
+
|
|
15791
16019
|
// src/client.ts
|
|
15792
16020
|
var DEFAULT_CONVERSATION_SESSION_LIST_LIMIT = 100;
|
|
15793
16021
|
var MAX_CONVERSATION_SESSION_LIST_LIMIT = 500;
|
|
@@ -17631,6 +17859,7 @@ var EnvironmentSession = class extends Session {
|
|
|
17631
17859
|
sessionDataHeaders;
|
|
17632
17860
|
beforeMutationSend;
|
|
17633
17861
|
heartbeatTimer = null;
|
|
17862
|
+
heartbeatProbeInFlight = false;
|
|
17634
17863
|
/** The last known graph container status, updated by checkReadiness(). */
|
|
17635
17864
|
graphContainerStatus = null;
|
|
17636
17865
|
constructor(client, environment, clientId, options = {}) {
|
|
@@ -17650,16 +17879,34 @@ var EnvironmentSession = class extends Session {
|
|
|
17650
17879
|
* an application RPC may be delayed by Durable Object work even while the
|
|
17651
17880
|
* underlying WebSocket is healthy.
|
|
17652
17881
|
*/
|
|
17653
|
-
startHeartbeat() {
|
|
17882
|
+
startHeartbeat(options = {}) {
|
|
17654
17883
|
if (this.heartbeatTimer) return;
|
|
17655
|
-
|
|
17656
|
-
|
|
17657
|
-
|
|
17658
|
-
|
|
17659
|
-
|
|
17660
|
-
|
|
17661
|
-
|
|
17662
|
-
|
|
17884
|
+
const probeServer = options.probeServer === true;
|
|
17885
|
+
this.heartbeatTimer = setInterval(
|
|
17886
|
+
() => {
|
|
17887
|
+
if (!this.client.isTransportOpen()) return;
|
|
17888
|
+
if (probeServer) {
|
|
17889
|
+
if (this.heartbeatProbeInFlight) return;
|
|
17890
|
+
if (this.client.hasPendingRpcRequests()) return;
|
|
17891
|
+
if (this.client.hasRecentInboundActivity(1e4)) return;
|
|
17892
|
+
this.heartbeatProbeInFlight = true;
|
|
17893
|
+
void this.client.call("client.heartbeat", {}).catch((error) => {
|
|
17894
|
+
if (this.client.isTransportOpen()) {
|
|
17895
|
+
this.client.reportTransportFailure(error);
|
|
17896
|
+
}
|
|
17897
|
+
}).finally(() => {
|
|
17898
|
+
this.heartbeatProbeInFlight = false;
|
|
17899
|
+
});
|
|
17900
|
+
return;
|
|
17901
|
+
}
|
|
17902
|
+
if (this.client.hasRecentInboundActivity(3e4)) return;
|
|
17903
|
+
if (this.client.hasRecentlyPendingRpcPeriod(6e4)) return;
|
|
17904
|
+
this.client.reportTransportFailure(
|
|
17905
|
+
new Error("No server activity received for 30 seconds")
|
|
17906
|
+
);
|
|
17907
|
+
},
|
|
17908
|
+
probeServer ? 1e4 : 5e3
|
|
17909
|
+
);
|
|
17663
17910
|
this.heartbeatTimer.unref?.();
|
|
17664
17911
|
}
|
|
17665
17912
|
stopHeartbeat() {
|
|
@@ -17667,10 +17914,15 @@ var EnvironmentSession = class extends Session {
|
|
|
17667
17914
|
clearInterval(this.heartbeatTimer);
|
|
17668
17915
|
this.heartbeatTimer = null;
|
|
17669
17916
|
}
|
|
17917
|
+
this.heartbeatProbeInFlight = false;
|
|
17670
17918
|
}
|
|
17671
17919
|
async hello() {
|
|
17672
17920
|
const result = await super.hello();
|
|
17673
|
-
|
|
17921
|
+
if (result.features?.serverHeartbeat === false) {
|
|
17922
|
+
this.startHeartbeat({ probeServer: true });
|
|
17923
|
+
} else {
|
|
17924
|
+
this.startHeartbeat();
|
|
17925
|
+
}
|
|
17674
17926
|
return result;
|
|
17675
17927
|
}
|
|
17676
17928
|
get environmentId() {
|
|
@@ -17856,7 +18108,10 @@ var EnvironmentSession = class extends Session {
|
|
|
17856
18108
|
queryOptions.targetId = options.target.id;
|
|
17857
18109
|
delete queryOptions.target;
|
|
17858
18110
|
}
|
|
17859
|
-
return this.sessionDataRequest("/artifacts", queryOptions)
|
|
18111
|
+
return this.sessionDataRequest("/artifacts", queryOptions).then((page) => ({
|
|
18112
|
+
...page,
|
|
18113
|
+
items: page.items.map(requireArtifactWorkflow)
|
|
18114
|
+
}));
|
|
17860
18115
|
},
|
|
17861
18116
|
listForLatestJob: (options = {}) => this.artifacts.list({
|
|
17862
18117
|
...options,
|
|
@@ -17869,7 +18124,7 @@ var EnvironmentSession = class extends Session {
|
|
|
17869
18124
|
// `cache: no-store`; this also bypasses intermediaries that ignore
|
|
17870
18125
|
// the Fetch cache directive for an otherwise identical GET URL.
|
|
17871
18126
|
{ _granularLiveRead: Date.now() }
|
|
17872
|
-
),
|
|
18127
|
+
).then(requireArtifactWorkflow),
|
|
17873
18128
|
create: (artifact) => this.sessionDataRequest(
|
|
17874
18129
|
"/artifacts",
|
|
17875
18130
|
void 0,
|
|
@@ -17877,7 +18132,7 @@ var EnvironmentSession = class extends Session {
|
|
|
17877
18132
|
method: "POST",
|
|
17878
18133
|
body: artifact
|
|
17879
18134
|
}
|
|
17880
|
-
),
|
|
18135
|
+
).then(requireArtifactWorkflow),
|
|
17881
18136
|
acceptSuggestion: (sequence) => this.sessionDataRequest(
|
|
17882
18137
|
"/artifacts/suggestions/accept",
|
|
17883
18138
|
void 0,
|
|
@@ -17885,7 +18140,7 @@ var EnvironmentSession = class extends Session {
|
|
|
17885
18140
|
method: "POST",
|
|
17886
18141
|
body: { sequence }
|
|
17887
18142
|
}
|
|
17888
|
-
),
|
|
18143
|
+
).then(requireArtifactWorkflow),
|
|
17889
18144
|
updateInputs: (artifactId, patch) => this.sessionDataRequest(
|
|
17890
18145
|
`/artifacts/${encodeURIComponent(artifactId)}`,
|
|
17891
18146
|
void 0,
|
|
@@ -17893,7 +18148,7 @@ var EnvironmentSession = class extends Session {
|
|
|
17893
18148
|
method: "PATCH",
|
|
17894
18149
|
body: patch
|
|
17895
18150
|
}
|
|
17896
|
-
),
|
|
18151
|
+
).then(requireArtifactWorkflow),
|
|
17897
18152
|
relationshipOptions: (artifactId, input) => this.sessionDataRequest(
|
|
17898
18153
|
`/artifacts/${encodeURIComponent(artifactId)}/relationship-options`,
|
|
17899
18154
|
void 0,
|
|
@@ -17903,27 +18158,27 @@ var EnvironmentSession = class extends Session {
|
|
|
17903
18158
|
`/artifacts/${encodeURIComponent(artifactId)}/relationship-create`,
|
|
17904
18159
|
void 0,
|
|
17905
18160
|
{ method: "POST", body: input }
|
|
17906
|
-
),
|
|
18161
|
+
).then(requireArtifactWorkflow),
|
|
17907
18162
|
validate: (artifactId) => this.sessionDataRequest(
|
|
17908
18163
|
`/artifacts/${encodeURIComponent(artifactId)}/validate`,
|
|
17909
18164
|
void 0,
|
|
17910
18165
|
{ method: "POST" }
|
|
17911
|
-
),
|
|
18166
|
+
).then(requireArtifactWorkflowResult),
|
|
17912
18167
|
execute: (artifactId, options) => this.sessionDataRequest(
|
|
17913
18168
|
`/artifacts/${encodeURIComponent(artifactId)}/execute`,
|
|
17914
18169
|
void 0,
|
|
17915
18170
|
{ method: "POST", body: options }
|
|
17916
|
-
),
|
|
18171
|
+
).then(requireArtifactWorkflowResult),
|
|
17917
18172
|
approve: (artifactId, options) => this.sessionDataRequest(
|
|
17918
18173
|
`/artifacts/${encodeURIComponent(artifactId)}/approve`,
|
|
17919
18174
|
void 0,
|
|
17920
18175
|
{ method: "POST", body: options }
|
|
17921
|
-
),
|
|
18176
|
+
).then(requireArtifactWorkflowResult),
|
|
17922
18177
|
cancel: (artifactId) => this.sessionDataRequest(
|
|
17923
18178
|
`/artifacts/${encodeURIComponent(artifactId)}/cancel`,
|
|
17924
18179
|
void 0,
|
|
17925
18180
|
{ method: "POST" }
|
|
17926
|
-
)
|
|
18181
|
+
).then(requireArtifactWorkflow)
|
|
17927
18182
|
};
|
|
17928
18183
|
}
|
|
17929
18184
|
get manualActions() {
|
|
@@ -22637,6 +22892,10 @@ ${actionIndex}
|
|
|
22637
22892
|
- 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.
|
|
22638
22893
|
- 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.
|
|
22639
22894
|
- For creation inputs, use the declared action schema and its field descriptions as the mapping authority. Copy every compatible value explicitly present in the current request, conversation, selected/saved object, file, fresh read, or declared ontology default, including values for fields with arbitrary names. Omit only values for which that context provides no evidence.
|
|
22895
|
+
- Preserve explicit qualifiers from the user's requested object identity, including quantities, product names, and named organizational scope. Do not shorten a title in a way that drops those qualifiers when they distinguish what is being created.
|
|
22896
|
+
- A contextual or ontology default may fill a value only when the user supplied no conflicting intent for that semantic role. If the user names a team, owner, account, location, or other scope that cannot be grounded, leave that relationship unresolved; never substitute the current actor's default or another convenient visible record.
|
|
22897
|
+
- A missing or inaccessible required relationship is not a reason to replace a reviewable action with a lookup-failure reply. Call the reviewable action with every grounded non-relationship value, omit the unresolved relationship, and let the artifact collect it from the user. This applies even when the user explicitly named the unavailable relationship.
|
|
22898
|
+
- Choose enum values from the request's meaning and the ontology-authored option semantics. Do not choose an enum merely because it makes an available default relationship, budget, policy, or downstream option compatible. If no enum is semantically supported, leave it unresolved or ask.
|
|
22640
22899
|
- Never fill an input because of its field name, a regular expression or other string pattern, its primitive type, a schema minimum, an example value, or the first enum option. If the evidence is ambiguous, leave the field unresolved or ask the user.
|
|
22641
22900
|
- 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.
|
|
22642
22901
|
- 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.
|