@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/cli/index.js
CHANGED
|
@@ -60,11 +60,11 @@ var __export = (target, all) => {
|
|
|
60
60
|
for (var name in all)
|
|
61
61
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
62
62
|
};
|
|
63
|
-
var __copyProps = (to,
|
|
64
|
-
if (
|
|
65
|
-
for (let key of __getOwnPropNames(
|
|
63
|
+
var __copyProps = (to, from2, except, desc) => {
|
|
64
|
+
if (from2 && typeof from2 === "object" || typeof from2 === "function") {
|
|
65
|
+
for (let key of __getOwnPropNames(from2))
|
|
66
66
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
67
|
-
__defProp(to, key, { get: () =>
|
|
67
|
+
__defProp(to, key, { get: () => from2[key], enumerable: !(desc = __getOwnPropDesc(from2, key)) || desc.enumerable });
|
|
68
68
|
}
|
|
69
69
|
return to;
|
|
70
70
|
};
|
|
@@ -8889,7 +8889,7 @@ createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
|
8889
8889
|
var source_default = chalk;
|
|
8890
8890
|
|
|
8891
8891
|
// ../../node_modules/mimic-function/index.js
|
|
8892
|
-
var copyProperty = (to,
|
|
8892
|
+
var copyProperty = (to, from2, property, ignoreNonConfigurable) => {
|
|
8893
8893
|
if (property === "length" || property === "prototype") {
|
|
8894
8894
|
return;
|
|
8895
8895
|
}
|
|
@@ -8897,7 +8897,7 @@ var copyProperty = (to, from, property, ignoreNonConfigurable) => {
|
|
|
8897
8897
|
return;
|
|
8898
8898
|
}
|
|
8899
8899
|
const toDescriptor = Object.getOwnPropertyDescriptor(to, property);
|
|
8900
|
-
const fromDescriptor = Object.getOwnPropertyDescriptor(
|
|
8900
|
+
const fromDescriptor = Object.getOwnPropertyDescriptor(from2, property);
|
|
8901
8901
|
if (!canCopyProperty(toDescriptor, fromDescriptor) && ignoreNonConfigurable) {
|
|
8902
8902
|
return;
|
|
8903
8903
|
}
|
|
@@ -8906,8 +8906,8 @@ var copyProperty = (to, from, property, ignoreNonConfigurable) => {
|
|
|
8906
8906
|
var canCopyProperty = function(toDescriptor, fromDescriptor) {
|
|
8907
8907
|
return toDescriptor === void 0 || toDescriptor.configurable || toDescriptor.writable === fromDescriptor.writable && toDescriptor.enumerable === fromDescriptor.enumerable && toDescriptor.configurable === fromDescriptor.configurable && (toDescriptor.writable || toDescriptor.value === fromDescriptor.value);
|
|
8908
8908
|
};
|
|
8909
|
-
var changePrototype = (to,
|
|
8910
|
-
const fromPrototype = Object.getPrototypeOf(
|
|
8909
|
+
var changePrototype = (to, from2) => {
|
|
8910
|
+
const fromPrototype = Object.getPrototypeOf(from2);
|
|
8911
8911
|
if (fromPrototype === Object.getPrototypeOf(to)) {
|
|
8912
8912
|
return;
|
|
8913
8913
|
}
|
|
@@ -8917,20 +8917,20 @@ var wrappedToString = (withName, fromBody) => `/* Wrapped ${withName}*/
|
|
|
8917
8917
|
${fromBody}`;
|
|
8918
8918
|
var toStringDescriptor = Object.getOwnPropertyDescriptor(Function.prototype, "toString");
|
|
8919
8919
|
var toStringName = Object.getOwnPropertyDescriptor(Function.prototype.toString, "name");
|
|
8920
|
-
var changeToString = (to,
|
|
8920
|
+
var changeToString = (to, from2, name) => {
|
|
8921
8921
|
const withName = name === "" ? "" : `with ${name.trim()}() `;
|
|
8922
|
-
const newToString = wrappedToString.bind(null, withName,
|
|
8922
|
+
const newToString = wrappedToString.bind(null, withName, from2.toString());
|
|
8923
8923
|
Object.defineProperty(newToString, "name", toStringName);
|
|
8924
8924
|
const { writable, enumerable, configurable } = toStringDescriptor;
|
|
8925
8925
|
Object.defineProperty(to, "toString", { value: newToString, writable, enumerable, configurable });
|
|
8926
8926
|
};
|
|
8927
|
-
function mimicFunction(to,
|
|
8927
|
+
function mimicFunction(to, from2, { ignoreNonConfigurable = false } = {}) {
|
|
8928
8928
|
const { name } = to;
|
|
8929
|
-
for (const property of Reflect.ownKeys(
|
|
8930
|
-
copyProperty(to,
|
|
8929
|
+
for (const property of Reflect.ownKeys(from2)) {
|
|
8930
|
+
copyProperty(to, from2, property, ignoreNonConfigurable);
|
|
8931
8931
|
}
|
|
8932
|
-
changePrototype(to,
|
|
8933
|
-
changeToString(to,
|
|
8932
|
+
changePrototype(to, from2);
|
|
8933
|
+
changeToString(to, from2, name);
|
|
8934
8934
|
return to;
|
|
8935
8935
|
}
|
|
8936
8936
|
|
|
@@ -9880,7 +9880,7 @@ function relationshipEdgesForClass(className, rels) {
|
|
|
9880
9880
|
return edges;
|
|
9881
9881
|
}
|
|
9882
9882
|
function toPascalCase(name) {
|
|
9883
|
-
return name.charAt(0).toUpperCase() +
|
|
9883
|
+
return name.split(/[_:\-\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
9884
9884
|
}
|
|
9885
9885
|
|
|
9886
9886
|
// src/cli/agent-docs/concept-blocks.ts
|
|
@@ -14065,16 +14065,107 @@ var StateMachineStateSchema = external_exports.union([
|
|
|
14065
14065
|
external_exports.string(),
|
|
14066
14066
|
external_exports.object({
|
|
14067
14067
|
name: external_exports.string().min(1),
|
|
14068
|
+
label: external_exports.string().optional(),
|
|
14069
|
+
description: external_exports.string().optional(),
|
|
14068
14070
|
isFinal: external_exports.boolean().optional()
|
|
14069
14071
|
}).strict()
|
|
14070
14072
|
]);
|
|
14073
|
+
var StateTransitionInputBindingSchema = external_exports.lazy(
|
|
14074
|
+
() => external_exports.union([
|
|
14075
|
+
external_exports.null(),
|
|
14076
|
+
external_exports.string(),
|
|
14077
|
+
external_exports.number(),
|
|
14078
|
+
external_exports.boolean(),
|
|
14079
|
+
external_exports.array(StateTransitionInputBindingSchema),
|
|
14080
|
+
external_exports.object({
|
|
14081
|
+
const: external_exports.unknown()
|
|
14082
|
+
}).strict(),
|
|
14083
|
+
external_exports.object({
|
|
14084
|
+
from: external_exports.literal("object"),
|
|
14085
|
+
path: external_exports.string().min(1),
|
|
14086
|
+
editable: external_exports.boolean().optional()
|
|
14087
|
+
}).strict(),
|
|
14088
|
+
external_exports.object({
|
|
14089
|
+
from: external_exports.literal("field"),
|
|
14090
|
+
name: external_exports.string().min(1),
|
|
14091
|
+
editable: external_exports.boolean().optional()
|
|
14092
|
+
}).strict(),
|
|
14093
|
+
external_exports.object({
|
|
14094
|
+
from: external_exports.literal("relationship"),
|
|
14095
|
+
name: external_exports.string().min(1),
|
|
14096
|
+
path: external_exports.string().min(1).optional(),
|
|
14097
|
+
many: external_exports.boolean().optional(),
|
|
14098
|
+
editable: external_exports.boolean().optional()
|
|
14099
|
+
}).strict(),
|
|
14100
|
+
external_exports.object({
|
|
14101
|
+
from: external_exports.literal("session"),
|
|
14102
|
+
path: external_exports.string().min(1),
|
|
14103
|
+
editable: external_exports.boolean().optional()
|
|
14104
|
+
}).strict(),
|
|
14105
|
+
external_exports.object({
|
|
14106
|
+
from: external_exports.literal("actor"),
|
|
14107
|
+
path: external_exports.string().min(1),
|
|
14108
|
+
editable: external_exports.boolean().optional()
|
|
14109
|
+
}).strict(),
|
|
14110
|
+
external_exports.record(external_exports.string(), StateTransitionInputBindingSchema)
|
|
14111
|
+
])
|
|
14112
|
+
);
|
|
14113
|
+
var StateTransitionActionSchema = external_exports.object({
|
|
14114
|
+
effect: external_exports.string().min(1),
|
|
14115
|
+
input: external_exports.record(external_exports.string(), StateTransitionInputBindingSchema).optional()
|
|
14116
|
+
}).strict();
|
|
14117
|
+
var StateTransitionAssigneeSchema = external_exports.object({
|
|
14118
|
+
kind: external_exports.string().min(1),
|
|
14119
|
+
from: StateTransitionInputBindingSchema.optional(),
|
|
14120
|
+
role: external_exports.string().optional(),
|
|
14121
|
+
label: external_exports.string().optional()
|
|
14122
|
+
}).strict();
|
|
14123
|
+
var StateTransitionRelatedStateRequirementSchema = external_exports.object({
|
|
14124
|
+
relationship: external_exports.string().min(1),
|
|
14125
|
+
machine: external_exports.string().min(1),
|
|
14126
|
+
state: external_exports.string().min(1),
|
|
14127
|
+
className: external_exports.string().min(1).optional(),
|
|
14128
|
+
label: external_exports.string().optional(),
|
|
14129
|
+
mode: external_exports.enum(["every", "some", "any"]).optional()
|
|
14130
|
+
}).strict();
|
|
14131
|
+
var StateTransitionRequirementsSchema = external_exports.object({
|
|
14132
|
+
fields: external_exports.array(external_exports.string().min(1)).optional(),
|
|
14133
|
+
relationships: external_exports.array(external_exports.string().min(1)).optional(),
|
|
14134
|
+
relatedStates: external_exports.array(StateTransitionRelatedStateRequirementSchema).optional()
|
|
14135
|
+
}).strict();
|
|
14136
|
+
var StateTransitionPermissionSchema = external_exports.union([
|
|
14137
|
+
external_exports.string().min(1),
|
|
14138
|
+
external_exports.object({
|
|
14139
|
+
profile: external_exports.string().min(1).optional(),
|
|
14140
|
+
profileId: external_exports.string().min(1).optional(),
|
|
14141
|
+
label: external_exports.string().optional(),
|
|
14142
|
+
reason: external_exports.string().optional()
|
|
14143
|
+
}).strict()
|
|
14144
|
+
]);
|
|
14145
|
+
var StateTransitionExpectedOutcomeSchema = external_exports.union([
|
|
14146
|
+
external_exports.string().min(1),
|
|
14147
|
+
external_exports.object({
|
|
14148
|
+
machine: external_exports.string().min(1).optional(),
|
|
14149
|
+
state: external_exports.string().min(1),
|
|
14150
|
+
summary: external_exports.string().optional()
|
|
14151
|
+
}).strict()
|
|
14152
|
+
]);
|
|
14071
14153
|
var StateMachineTransitionSchema = external_exports.object({
|
|
14072
14154
|
name: external_exports.string().min(1),
|
|
14073
14155
|
from: external_exports.string().min(1),
|
|
14074
|
-
to: external_exports.string().min(1)
|
|
14156
|
+
to: external_exports.string().min(1),
|
|
14157
|
+
label: external_exports.string().optional(),
|
|
14158
|
+
description: external_exports.string().optional(),
|
|
14159
|
+
action: StateTransitionActionSchema.optional(),
|
|
14160
|
+
assignee: StateTransitionAssigneeSchema.optional(),
|
|
14161
|
+
requirements: StateTransitionRequirementsSchema.optional(),
|
|
14162
|
+
permission: StateTransitionPermissionSchema.optional(),
|
|
14163
|
+
risk: external_exports.enum(["low", "medium", "high"]).optional(),
|
|
14164
|
+
expectedOutcome: StateTransitionExpectedOutcomeSchema.optional()
|
|
14075
14165
|
}).strict();
|
|
14076
14166
|
external_exports.object({
|
|
14077
14167
|
name: external_exports.string().min(1),
|
|
14168
|
+
stateField: external_exports.string().min(1).optional(),
|
|
14078
14169
|
entryState: external_exports.string().min(1),
|
|
14079
14170
|
states: external_exports.array(StateMachineStateSchema).min(1),
|
|
14080
14171
|
transitions: external_exports.array(StateMachineTransitionSchema),
|
|
@@ -14141,6 +14232,16 @@ var PoliciesSchema = external_exports.object({
|
|
|
14141
14232
|
confirmWhen: external_exports.array(PolicyRuleSchema).optional(),
|
|
14142
14233
|
denyWhen: external_exports.array(PolicyRuleSchema).optional()
|
|
14143
14234
|
}).strict();
|
|
14235
|
+
var CreatesSchema = external_exports.union([
|
|
14236
|
+
external_exports.string().min(1),
|
|
14237
|
+
external_exports.object({
|
|
14238
|
+
className: external_exports.string().min(1),
|
|
14239
|
+
idPath: external_exports.string().min(1).optional(),
|
|
14240
|
+
pathPath: external_exports.string().min(1).optional(),
|
|
14241
|
+
statePath: external_exports.string().min(1).optional(),
|
|
14242
|
+
classStateHandle: external_exports.boolean().optional()
|
|
14243
|
+
}).strict()
|
|
14244
|
+
]);
|
|
14144
14245
|
external_exports.object({
|
|
14145
14246
|
postCondition: external_exports.union([
|
|
14146
14247
|
external_exports.string(),
|
|
@@ -14171,6 +14272,7 @@ external_exports.object({
|
|
|
14171
14272
|
mode: external_exports.string().optional()
|
|
14172
14273
|
}).strict()
|
|
14173
14274
|
]).optional(),
|
|
14275
|
+
creates: CreatesSchema.optional(),
|
|
14174
14276
|
access: external_exports.enum(["read", "write", "ui"]).optional(),
|
|
14175
14277
|
effectKind: external_exports.enum(["read", "write", "ui"]).optional(),
|
|
14176
14278
|
sideEffect: external_exports.enum(["read", "write", "ui", "readonly", "read_only"]).optional(),
|
|
@@ -14398,6 +14500,7 @@ function mergeMethodSummaryPatch(target, patch) {
|
|
|
14398
14500
|
if (patch.metamodels !== void 0) target.metamodels = patch.metamodels;
|
|
14399
14501
|
if (patch.effectBehaviors !== void 0)
|
|
14400
14502
|
target.effectBehaviors = patch.effectBehaviors;
|
|
14503
|
+
if (patch.creates !== void 0) target.creates = patch.creates;
|
|
14401
14504
|
if (patch.static !== void 0) target.static = patch.static;
|
|
14402
14505
|
}
|
|
14403
14506
|
function toPascalCase2(value) {
|
|
@@ -14458,29 +14561,60 @@ function normalizeEffectBehaviorSummary(metamodels) {
|
|
|
14458
14561
|
}
|
|
14459
14562
|
return Object.keys(result).length > 0 ? result : null;
|
|
14460
14563
|
}
|
|
14461
|
-
function
|
|
14462
|
-
if (!
|
|
14463
|
-
|
|
14564
|
+
function normalizeCreationSummary(metamodels) {
|
|
14565
|
+
if (!isObject(metamodels)) return null;
|
|
14566
|
+
let raw = metamodels.creates;
|
|
14567
|
+
if (typeof raw === "string" && raw.trim().length > 0) {
|
|
14568
|
+
const trimmed = raw.trim();
|
|
14569
|
+
if (trimmed.startsWith("{") || trimmed.startsWith('"')) {
|
|
14570
|
+
try {
|
|
14571
|
+
raw = JSON.parse(trimmed);
|
|
14572
|
+
} catch {
|
|
14573
|
+
return { className: trimmed };
|
|
14574
|
+
}
|
|
14575
|
+
} else {
|
|
14576
|
+
return { className: trimmed };
|
|
14577
|
+
}
|
|
14578
|
+
}
|
|
14579
|
+
if (typeof raw === "string" && raw.trim().length > 0) {
|
|
14580
|
+
return { className: raw.trim() };
|
|
14464
14581
|
}
|
|
14582
|
+
if (!isObject(raw)) return null;
|
|
14583
|
+
const className = typeof raw.className === "string" && raw.className.trim() ? raw.className.trim() : "";
|
|
14584
|
+
if (!className) return null;
|
|
14585
|
+
return {
|
|
14586
|
+
className,
|
|
14587
|
+
...typeof raw.idPath === "string" && raw.idPath.trim() ? { idPath: raw.idPath.trim() } : {},
|
|
14588
|
+
...typeof raw.pathPath === "string" && raw.pathPath.trim() ? { pathPath: raw.pathPath.trim() } : {},
|
|
14589
|
+
...typeof raw.statePath === "string" && raw.statePath.trim() ? { statePath: raw.statePath.trim() } : {},
|
|
14590
|
+
...typeof raw.classStateHandle === "boolean" ? { classStateHandle: raw.classStateHandle } : {}
|
|
14591
|
+
};
|
|
14592
|
+
}
|
|
14593
|
+
function buildEffectBehaviorDocs(effectBehaviors, creates) {
|
|
14465
14594
|
const docs = [];
|
|
14466
|
-
if (
|
|
14595
|
+
if (creates) {
|
|
14596
|
+
docs.push(
|
|
14597
|
+
`Creation method: creates ${creates.className}. The agent may use generated class-level new-record action methods for this class.`
|
|
14598
|
+
);
|
|
14599
|
+
}
|
|
14600
|
+
if (effectBehaviors?.approvalRequired?.required) {
|
|
14467
14601
|
docs.push(
|
|
14468
14602
|
effectBehaviors.approvalRequired.reason ? `Approval required: ${effectBehaviors.approvalRequired.reason}.` : "Approval required before execution."
|
|
14469
14603
|
);
|
|
14470
14604
|
}
|
|
14471
|
-
if (effectBehaviors
|
|
14605
|
+
if (effectBehaviors?.postCondition) {
|
|
14472
14606
|
docs.push(`Post-condition: ${effectBehaviors.postCondition.condition}.`);
|
|
14473
14607
|
if (effectBehaviors.postCondition.description) {
|
|
14474
14608
|
docs.push(effectBehaviors.postCondition.description);
|
|
14475
14609
|
}
|
|
14476
14610
|
}
|
|
14477
|
-
if (effectBehaviors
|
|
14611
|
+
if (effectBehaviors?.dryRun?.enabled) {
|
|
14478
14612
|
docs.push("Supports dry run.");
|
|
14479
14613
|
if (effectBehaviors.dryRun.description) {
|
|
14480
14614
|
docs.push(effectBehaviors.dryRun.description);
|
|
14481
14615
|
}
|
|
14482
14616
|
}
|
|
14483
|
-
if (effectBehaviors
|
|
14617
|
+
if (effectBehaviors?.reverse) {
|
|
14484
14618
|
if (effectBehaviors.reverse.handler) {
|
|
14485
14619
|
docs.push(`Reverse handler: ${effectBehaviors.reverse.handler}.`);
|
|
14486
14620
|
} else {
|
|
@@ -14539,13 +14673,21 @@ function buildEffectBehaviorMutations(toolPath, spec) {
|
|
|
14539
14673
|
query: `mutation { at(path: ${JSON.stringify(toolPath)}) { set_approval_required(${args}) { kind } } }`
|
|
14540
14674
|
});
|
|
14541
14675
|
}
|
|
14676
|
+
if (spec.creates !== void 0) {
|
|
14677
|
+
mutations.push({
|
|
14678
|
+
label: `set creates on ${toolPath}`,
|
|
14679
|
+
query: `mutation { at(path: ${JSON.stringify(toolPath)}) { create_submodel(subpath: "creates", label: "creates") { set_string_value(value: ${JSON.stringify(
|
|
14680
|
+
JSON.stringify(spec.creates)
|
|
14681
|
+
)}) { done } } } }`
|
|
14682
|
+
});
|
|
14683
|
+
}
|
|
14542
14684
|
return mutations;
|
|
14543
14685
|
}
|
|
14544
14686
|
function readMethodEffectBehaviors(rawMethod) {
|
|
14687
|
+
const metamodels = isObject(rawMethod.metamodels) ? rawMethod.metamodels : null;
|
|
14545
14688
|
return {
|
|
14546
|
-
effectBehaviors: normalizeEffectBehaviorSummary(
|
|
14547
|
-
|
|
14548
|
-
)
|
|
14689
|
+
effectBehaviors: normalizeEffectBehaviorSummary(metamodels),
|
|
14690
|
+
creates: normalizeCreationSummary(metamodels)
|
|
14549
14691
|
};
|
|
14550
14692
|
}
|
|
14551
14693
|
var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
@@ -14567,6 +14709,10 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
14567
14709
|
{
|
|
14568
14710
|
key: "approvalRequired",
|
|
14569
14711
|
description: "Boolean or `{ required, reason, mode }`."
|
|
14712
|
+
},
|
|
14713
|
+
{
|
|
14714
|
+
key: "creates",
|
|
14715
|
+
description: 'Marks a static method as an allowed creator for a class. Use `creates: "class_name"` or `{ className, idPath, pathPath, statePath, classStateHandle }`.'
|
|
14570
14716
|
}
|
|
14571
14717
|
]
|
|
14572
14718
|
},
|
|
@@ -14686,7 +14832,10 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
14686
14832
|
...methodIR,
|
|
14687
14833
|
docs: [
|
|
14688
14834
|
...methodIR.docs,
|
|
14689
|
-
...buildEffectBehaviorDocs(
|
|
14835
|
+
...buildEffectBehaviorDocs(
|
|
14836
|
+
methodSummary.effectBehaviors,
|
|
14837
|
+
methodSummary.creates
|
|
14838
|
+
)
|
|
14690
14839
|
]
|
|
14691
14840
|
};
|
|
14692
14841
|
}
|
|
@@ -15305,15 +15454,47 @@ var searchableMetamodelPackage = defineMetamodelPackage({
|
|
|
15305
15454
|
|
|
15306
15455
|
// ../metamodel-state-machine/src/index.ts
|
|
15307
15456
|
function normalizeStateMachines(values) {
|
|
15457
|
+
const parseJsonRecord = (value) => {
|
|
15458
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
15459
|
+
return value;
|
|
15460
|
+
}
|
|
15461
|
+
if (typeof value !== "string" || !value.trim()) return null;
|
|
15462
|
+
try {
|
|
15463
|
+
const parsed = JSON.parse(value);
|
|
15464
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
15465
|
+
} catch {
|
|
15466
|
+
return null;
|
|
15467
|
+
}
|
|
15468
|
+
};
|
|
15469
|
+
const parseJsonValue = (value) => {
|
|
15470
|
+
if (value === null || typeof value === "undefined") return null;
|
|
15471
|
+
if (typeof value !== "string") return value;
|
|
15472
|
+
if (!value.trim()) return null;
|
|
15473
|
+
try {
|
|
15474
|
+
return JSON.parse(value);
|
|
15475
|
+
} catch {
|
|
15476
|
+
return value;
|
|
15477
|
+
}
|
|
15478
|
+
};
|
|
15308
15479
|
return (values || []).map((machine) => {
|
|
15309
15480
|
const states = (machine?.states || []).map((state) => ({
|
|
15310
15481
|
name: String(state?.name || ""),
|
|
15311
|
-
|
|
15482
|
+
label: typeof state?.label === "string" ? state.label : null,
|
|
15483
|
+
description: typeof state?.description === "string" ? state.description : null,
|
|
15484
|
+
isFinal: Boolean(state?.is_final ?? state?.isFinal)
|
|
15312
15485
|
})).filter((state) => state.name.length > 0);
|
|
15313
15486
|
const transitions = (machine?.transitions || []).map((transition) => ({
|
|
15314
15487
|
name: String(transition?.name || ""),
|
|
15315
15488
|
from: String(transition?.from?.name || ""),
|
|
15316
|
-
to: String(transition?.to?.name || "")
|
|
15489
|
+
to: String(transition?.to?.name || ""),
|
|
15490
|
+
label: typeof transition?.label === "string" ? transition.label : null,
|
|
15491
|
+
description: typeof transition?.description === "string" ? transition.description : null,
|
|
15492
|
+
action: parseJsonRecord(transition?.action) || parseJsonRecord(transition?.action_json),
|
|
15493
|
+
assignee: parseJsonRecord(transition?.assignee) || parseJsonRecord(transition?.assignee_json),
|
|
15494
|
+
requirements: parseJsonRecord(transition?.requirements) || parseJsonRecord(transition?.requirements_json),
|
|
15495
|
+
permission: parseJsonValue(transition?.permission) ?? parseJsonValue(transition?.permission_json),
|
|
15496
|
+
risk: transition?.risk === "low" || transition?.risk === "medium" || transition?.risk === "high" ? transition.risk : null,
|
|
15497
|
+
expectedOutcome: parseJsonValue(transition?.expectedOutcome) ?? parseJsonValue(transition?.expected_outcome_json)
|
|
15317
15498
|
})).filter(
|
|
15318
15499
|
(transition) => transition.name.length > 0 && transition.from.length > 0 && transition.to.length > 0
|
|
15319
15500
|
);
|
|
@@ -15335,6 +15516,15 @@ function transitionTypeName(className, machineName) {
|
|
|
15335
15516
|
function pathTypeName(className, machineName) {
|
|
15336
15517
|
return `${stateTypeName(className, machineName)}Path`;
|
|
15337
15518
|
}
|
|
15519
|
+
function methodToken(value) {
|
|
15520
|
+
const token = String(value || "").trim().replace(/[^A-Za-z0-9_]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
15521
|
+
return token || "state";
|
|
15522
|
+
}
|
|
15523
|
+
function transitionActionsForMachine(machine) {
|
|
15524
|
+
return Object.fromEntries(
|
|
15525
|
+
(machine.transitions || []).filter((transition) => transition.action?.effect).map((transition) => [transition.name, transition.action])
|
|
15526
|
+
);
|
|
15527
|
+
}
|
|
15338
15528
|
function normalizeStateDefinitions(machine) {
|
|
15339
15529
|
const finalStates = new Set(machine.finalStates || []);
|
|
15340
15530
|
const states = /* @__PURE__ */ new Map();
|
|
@@ -15348,6 +15538,8 @@ function normalizeStateDefinitions(machine) {
|
|
|
15348
15538
|
}
|
|
15349
15539
|
states.set(rawState.name, {
|
|
15350
15540
|
name: rawState.name,
|
|
15541
|
+
label: rawState.label,
|
|
15542
|
+
description: rawState.description,
|
|
15351
15543
|
isFinal: Boolean(rawState.isFinal) || finalStates.has(rawState.name)
|
|
15352
15544
|
});
|
|
15353
15545
|
}
|
|
@@ -15359,6 +15551,44 @@ function normalizeStateDefinitions(machine) {
|
|
|
15359
15551
|
}
|
|
15360
15552
|
return [...states.values()];
|
|
15361
15553
|
}
|
|
15554
|
+
function transitionMetadataGraphqlArgs(transition) {
|
|
15555
|
+
const args = [];
|
|
15556
|
+
if (typeof transition.label === "string") {
|
|
15557
|
+
args.push(`label: ${JSON.stringify(transition.label)}`);
|
|
15558
|
+
}
|
|
15559
|
+
if (typeof transition.description === "string") {
|
|
15560
|
+
args.push(`description: ${JSON.stringify(transition.description)}`);
|
|
15561
|
+
}
|
|
15562
|
+
if (transition.action) {
|
|
15563
|
+
args.push(
|
|
15564
|
+
`action_json: ${JSON.stringify(JSON.stringify(transition.action))}`
|
|
15565
|
+
);
|
|
15566
|
+
}
|
|
15567
|
+
if (transition.assignee) {
|
|
15568
|
+
args.push(
|
|
15569
|
+
`assignee_json: ${JSON.stringify(JSON.stringify(transition.assignee))}`
|
|
15570
|
+
);
|
|
15571
|
+
}
|
|
15572
|
+
if (transition.requirements) {
|
|
15573
|
+
args.push(
|
|
15574
|
+
`requirements_json: ${JSON.stringify(JSON.stringify(transition.requirements))}`
|
|
15575
|
+
);
|
|
15576
|
+
}
|
|
15577
|
+
if (transition.permission) {
|
|
15578
|
+
args.push(
|
|
15579
|
+
`permission_json: ${JSON.stringify(JSON.stringify(transition.permission))}`
|
|
15580
|
+
);
|
|
15581
|
+
}
|
|
15582
|
+
if (transition.risk) {
|
|
15583
|
+
args.push(`risk: ${JSON.stringify(transition.risk)}`);
|
|
15584
|
+
}
|
|
15585
|
+
if (transition.expectedOutcome) {
|
|
15586
|
+
args.push(
|
|
15587
|
+
`expected_outcome_json: ${JSON.stringify(JSON.stringify(transition.expectedOutcome))}`
|
|
15588
|
+
);
|
|
15589
|
+
}
|
|
15590
|
+
return args.length > 0 ? `, ${args.join(", ")}` : "";
|
|
15591
|
+
}
|
|
15362
15592
|
function buildStateMachineModelMutations(modelPath, machines) {
|
|
15363
15593
|
const mutations = [];
|
|
15364
15594
|
for (const machine of machines || []) {
|
|
@@ -15369,12 +15599,13 @@ function buildStateMachineModelMutations(modelPath, machines) {
|
|
|
15369
15599
|
)}, entry_state: ${JSON.stringify(machine.entryState)}) { name } } }`
|
|
15370
15600
|
});
|
|
15371
15601
|
for (const state of normalizeStateDefinitions(machine)) {
|
|
15372
|
-
if (state.name === machine.entryState && !state.isFinal)
|
|
15602
|
+
if (state.name === machine.entryState && !state.isFinal && !state.label && !state.description)
|
|
15603
|
+
continue;
|
|
15373
15604
|
mutations.push({
|
|
15374
15605
|
label: `add state ${state.name} on ${modelPath}.${machine.name}`,
|
|
15375
15606
|
query: `mutation { at(path: ${JSON.stringify(modelPath)}) { state_machine(name: ${JSON.stringify(
|
|
15376
15607
|
machine.name
|
|
15377
|
-
)}) { add_state(name: ${JSON.stringify(state.name)}, is_final: ${state.isFinal}) { name } } } }`
|
|
15608
|
+
)}) { add_state(name: ${JSON.stringify(state.name)}, is_final: ${state.isFinal}, label: ${JSON.stringify(state.label || null)}, description: ${JSON.stringify(state.description || null)}) { name } } } }`
|
|
15378
15609
|
});
|
|
15379
15610
|
}
|
|
15380
15611
|
for (const transition of machine.transitions || []) {
|
|
@@ -15386,18 +15617,27 @@ function buildStateMachineModelMutations(modelPath, machines) {
|
|
|
15386
15617
|
transition.name
|
|
15387
15618
|
)}, from: ${JSON.stringify(transition.from)}, to: ${JSON.stringify(
|
|
15388
15619
|
transition.to
|
|
15389
|
-
)}) { name } } } }`
|
|
15620
|
+
)}${transitionMetadataGraphqlArgs(transition)}) { name } } } }`
|
|
15390
15621
|
});
|
|
15391
15622
|
}
|
|
15392
15623
|
}
|
|
15393
15624
|
return mutations;
|
|
15394
15625
|
}
|
|
15395
15626
|
function buildMachineTypes(classSummary, machine) {
|
|
15627
|
+
const stateGlossary = machine.states.map((state) => {
|
|
15628
|
+
const label = state.label && state.label !== state.name ? state.label : null;
|
|
15629
|
+
const meaning = [label, state.description].filter(Boolean).join(" \u2014 ");
|
|
15630
|
+
const finalMarker = state.isFinal ? " Final state." : "";
|
|
15631
|
+
return `${state.name}${meaning ? `: ${meaning}` : "."}${finalMarker}`;
|
|
15632
|
+
});
|
|
15396
15633
|
return [
|
|
15397
15634
|
{
|
|
15398
15635
|
kind: "union",
|
|
15399
15636
|
name: stateTypeName(classSummary.name, machine.name),
|
|
15400
|
-
docs: [
|
|
15637
|
+
docs: [
|
|
15638
|
+
`Allowed states for ${classSummary.name}.${machine.name}.`,
|
|
15639
|
+
...stateGlossary
|
|
15640
|
+
],
|
|
15401
15641
|
members: machine.states.map((state) => state.name)
|
|
15402
15642
|
},
|
|
15403
15643
|
{
|
|
@@ -15413,7 +15653,7 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
15413
15653
|
const transitionName = transitionTypeName(classSummary.name, machine.name);
|
|
15414
15654
|
pathTypeName(classSummary.name, machine.name);
|
|
15415
15655
|
const docsPrefix = `${classSummary.name}.${machine.name}`;
|
|
15416
|
-
|
|
15656
|
+
const methods = [
|
|
15417
15657
|
{
|
|
15418
15658
|
name: `get_${machine.name}`,
|
|
15419
15659
|
docs: [`Get the current ${docsPrefix} state.`],
|
|
@@ -15509,6 +15749,99 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
15509
15749
|
}
|
|
15510
15750
|
}
|
|
15511
15751
|
];
|
|
15752
|
+
const creationMethods = (classSummary.methods || []).filter(
|
|
15753
|
+
(method) => method.static === true && Boolean(method.creates) && method.creates?.className === classSummary.name && typeof method.effectKey === "string" && method.effectKey.length > 0
|
|
15754
|
+
);
|
|
15755
|
+
for (const state of machine.states) {
|
|
15756
|
+
const stateNameValue = typeof state === "string" ? state : String(state?.name || "");
|
|
15757
|
+
if (!stateNameValue) continue;
|
|
15758
|
+
const token = methodToken(stateNameValue);
|
|
15759
|
+
methods.push(
|
|
15760
|
+
{
|
|
15761
|
+
name: `reach_${machine.name}_to_${token}`,
|
|
15762
|
+
docs: [`Reach ${docsPrefix} state ${stateNameValue}.`],
|
|
15763
|
+
static: false,
|
|
15764
|
+
params: [],
|
|
15765
|
+
returnType: `Promise<${toPascalCase2(classSummary.name)}>`,
|
|
15766
|
+
runtime: {
|
|
15767
|
+
kind: "state_machine",
|
|
15768
|
+
machineName: machine.name,
|
|
15769
|
+
className: classSummary.name,
|
|
15770
|
+
stateTypeName: stateName,
|
|
15771
|
+
transitionTypeName: transitionName,
|
|
15772
|
+
operation: "reach",
|
|
15773
|
+
targetState: stateNameValue,
|
|
15774
|
+
transitionActions: transitionActionsForMachine(machine)
|
|
15775
|
+
}
|
|
15776
|
+
},
|
|
15777
|
+
{
|
|
15778
|
+
name: `prepare_${machine.name}_to_${token}`,
|
|
15779
|
+
docs: [
|
|
15780
|
+
`Prepare a reviewable artifact that can move ${docsPrefix} to ${stateNameValue}.`
|
|
15781
|
+
],
|
|
15782
|
+
static: false,
|
|
15783
|
+
params: [],
|
|
15784
|
+
returnType: "Promise<SessionArtifactRecord>",
|
|
15785
|
+
runtime: {
|
|
15786
|
+
kind: "state_machine",
|
|
15787
|
+
machineName: machine.name,
|
|
15788
|
+
className: classSummary.name,
|
|
15789
|
+
stateTypeName: stateName,
|
|
15790
|
+
transitionTypeName: transitionName,
|
|
15791
|
+
operation: "prepare_reach",
|
|
15792
|
+
targetState: stateNameValue,
|
|
15793
|
+
transitionActions: transitionActionsForMachine(machine)
|
|
15794
|
+
}
|
|
15795
|
+
}
|
|
15796
|
+
);
|
|
15797
|
+
for (const creationMethod of creationMethods) {
|
|
15798
|
+
const creationRuntime = {
|
|
15799
|
+
kind: "state_machine",
|
|
15800
|
+
machineName: machine.name,
|
|
15801
|
+
className: classSummary.name,
|
|
15802
|
+
stateTypeName: stateName,
|
|
15803
|
+
transitionTypeName: transitionName,
|
|
15804
|
+
operation: "prepare_create_reach",
|
|
15805
|
+
targetState: stateNameValue,
|
|
15806
|
+
transitionActions: transitionActionsForMachine(machine),
|
|
15807
|
+
creation: {
|
|
15808
|
+
methodName: creationMethod.name,
|
|
15809
|
+
effectKey: creationMethod.effectKey || creationMethod.name,
|
|
15810
|
+
inputSchema: creationMethod.inputSchema,
|
|
15811
|
+
outputSchema: creationMethod.outputSchema,
|
|
15812
|
+
creates: creationMethod.creates
|
|
15813
|
+
}
|
|
15814
|
+
};
|
|
15815
|
+
const viaName = `prepare_${machine.name}_to_${token}_via_${methodToken(creationMethod.name)}`;
|
|
15816
|
+
methods.push({
|
|
15817
|
+
name: viaName,
|
|
15818
|
+
docs: [
|
|
15819
|
+
`Prepare a reviewable artifact that will create a new ${classSummary.name} through ${creationMethod.name}, then move ${docsPrefix} to ${stateNameValue}.`
|
|
15820
|
+
],
|
|
15821
|
+
static: true,
|
|
15822
|
+
params: [
|
|
15823
|
+
{ name: "input", type: "Record<string, any>", optional: true }
|
|
15824
|
+
],
|
|
15825
|
+
returnType: "Promise<SessionArtifactRecord>",
|
|
15826
|
+
runtime: creationRuntime
|
|
15827
|
+
});
|
|
15828
|
+
if (creationMethods.length === 1) {
|
|
15829
|
+
methods.push({
|
|
15830
|
+
name: `prepare_${machine.name}_to_${token}`,
|
|
15831
|
+
docs: [
|
|
15832
|
+
`Prepare a reviewable artifact that will create a new ${classSummary.name}, then move ${docsPrefix} to ${stateNameValue}.`
|
|
15833
|
+
],
|
|
15834
|
+
static: true,
|
|
15835
|
+
params: [
|
|
15836
|
+
{ name: "input", type: "Record<string, any>", optional: true }
|
|
15837
|
+
],
|
|
15838
|
+
returnType: "Promise<SessionArtifactRecord>",
|
|
15839
|
+
runtime: creationRuntime
|
|
15840
|
+
});
|
|
15841
|
+
}
|
|
15842
|
+
}
|
|
15843
|
+
}
|
|
15844
|
+
return methods;
|
|
15512
15845
|
}
|
|
15513
15846
|
function readStateMachineSummaries(rawClass) {
|
|
15514
15847
|
return {
|
|
@@ -15531,8 +15864,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15531
15864
|
type StateMachineMutation {
|
|
15532
15865
|
name: String!
|
|
15533
15866
|
state_machine: StateMachine!
|
|
15534
|
-
add_state(name: String!, is_final: Boolean): StateMachineMutation!
|
|
15535
|
-
add_transition(name: String!, from: String!, to: String
|
|
15867
|
+
add_state(name: String!, is_final: Boolean, label: String, description: String): StateMachineMutation!
|
|
15868
|
+
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!
|
|
15536
15869
|
activate_transition(name: String!): StateMachineMutation!
|
|
15537
15870
|
}
|
|
15538
15871
|
|
|
@@ -15549,6 +15882,7 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15549
15882
|
type StateMachineSnapshotMutation {
|
|
15550
15883
|
snapshot: StateMachineSnapshot!
|
|
15551
15884
|
activate_transition(name: String!): StateMachineSnapshotMutation!
|
|
15885
|
+
observe_state(state: String!, force: Boolean, source: String): StateMachineSnapshotMutation!
|
|
15552
15886
|
}
|
|
15553
15887
|
|
|
15554
15888
|
type StateMachine {
|
|
@@ -15568,6 +15902,8 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15568
15902
|
|
|
15569
15903
|
type StateMachineState {
|
|
15570
15904
|
name: String!
|
|
15905
|
+
label: String
|
|
15906
|
+
description: String
|
|
15571
15907
|
is_final: Boolean!
|
|
15572
15908
|
}
|
|
15573
15909
|
|
|
@@ -15575,6 +15911,14 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15575
15911
|
name: String!
|
|
15576
15912
|
from: StateMachineState!
|
|
15577
15913
|
to: StateMachineState!
|
|
15914
|
+
label: String
|
|
15915
|
+
description: String
|
|
15916
|
+
action_json: String
|
|
15917
|
+
assignee_json: String
|
|
15918
|
+
requirements_json: String
|
|
15919
|
+
permission_json: String
|
|
15920
|
+
risk: String
|
|
15921
|
+
expected_outcome_json: String
|
|
15578
15922
|
}
|
|
15579
15923
|
|
|
15580
15924
|
type StateMachinePath {
|
|
@@ -15627,23 +15971,47 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15627
15971
|
StateMachineMutation: {
|
|
15628
15972
|
name: (value) => value.name,
|
|
15629
15973
|
state_machine: async (value) => await run(value.target.state_machine(value.name)),
|
|
15630
|
-
add_state: async (value, { name, is_final }) => {
|
|
15974
|
+
add_state: async (value, { name, is_final, label, description }) => {
|
|
15631
15975
|
await run(
|
|
15632
15976
|
value.target.add_state_machine_state(
|
|
15633
15977
|
value.name,
|
|
15634
15978
|
name,
|
|
15635
|
-
is_final ?? false
|
|
15979
|
+
is_final ?? false,
|
|
15980
|
+
label,
|
|
15981
|
+
description
|
|
15636
15982
|
)
|
|
15637
15983
|
);
|
|
15638
15984
|
return value;
|
|
15639
15985
|
},
|
|
15640
|
-
add_transition: async (value, {
|
|
15986
|
+
add_transition: async (value, {
|
|
15987
|
+
name,
|
|
15988
|
+
from: from2,
|
|
15989
|
+
to,
|
|
15990
|
+
label,
|
|
15991
|
+
description,
|
|
15992
|
+
action_json,
|
|
15993
|
+
assignee_json,
|
|
15994
|
+
requirements_json,
|
|
15995
|
+
permission_json,
|
|
15996
|
+
risk,
|
|
15997
|
+
expected_outcome_json
|
|
15998
|
+
}) => {
|
|
15641
15999
|
await run(
|
|
15642
16000
|
value.target.add_state_machine_transition(
|
|
15643
16001
|
value.name,
|
|
15644
16002
|
name,
|
|
15645
|
-
|
|
15646
|
-
to
|
|
16003
|
+
from2,
|
|
16004
|
+
to,
|
|
16005
|
+
{
|
|
16006
|
+
label,
|
|
16007
|
+
description,
|
|
16008
|
+
actionJson: action_json,
|
|
16009
|
+
assigneeJson: assignee_json,
|
|
16010
|
+
requirementsJson: requirements_json,
|
|
16011
|
+
permissionJson: permission_json,
|
|
16012
|
+
risk,
|
|
16013
|
+
expectedOutcomeJson: expected_outcome_json
|
|
16014
|
+
}
|
|
15647
16015
|
)
|
|
15648
16016
|
);
|
|
15649
16017
|
return value;
|
|
@@ -15662,16 +16030,37 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15662
16030
|
value.target.activate_state_machine_transition(value.name, name)
|
|
15663
16031
|
);
|
|
15664
16032
|
return value;
|
|
16033
|
+
},
|
|
16034
|
+
observe_state: async (value, { state, force, source }) => {
|
|
16035
|
+
await run(
|
|
16036
|
+
value.target.observe_state_machine_state(
|
|
16037
|
+
value.name,
|
|
16038
|
+
state,
|
|
16039
|
+
force === true,
|
|
16040
|
+
source
|
|
16041
|
+
)
|
|
16042
|
+
);
|
|
16043
|
+
return value;
|
|
15665
16044
|
}
|
|
15666
16045
|
},
|
|
15667
16046
|
StateMachineState: {
|
|
15668
16047
|
name: (value) => value.name,
|
|
16048
|
+
label: (value) => value.label || null,
|
|
16049
|
+
description: (value) => value.description || null,
|
|
15669
16050
|
is_final: (value) => value.is_final
|
|
15670
16051
|
},
|
|
15671
16052
|
StateMachineTransition: {
|
|
15672
16053
|
name: (value) => value.name,
|
|
15673
16054
|
from: (value) => value.from_state || { name: value.from, is_final: false },
|
|
15674
|
-
to: (value) => value.to_state || { name: value.to, is_final: false }
|
|
16055
|
+
to: (value) => value.to_state || { name: value.to, is_final: false },
|
|
16056
|
+
label: (value) => value.label || null,
|
|
16057
|
+
description: (value) => value.description || null,
|
|
16058
|
+
action_json: (value) => value.action_json || null,
|
|
16059
|
+
assignee_json: (value) => value.assignee_json || null,
|
|
16060
|
+
requirements_json: (value) => value.requirements_json || null,
|
|
16061
|
+
permission_json: (value) => value.permission_json || null,
|
|
16062
|
+
risk: (value) => value.risk || null,
|
|
16063
|
+
expected_outcome_json: (value) => value.expected_outcome_json || null
|
|
15675
16064
|
},
|
|
15676
16065
|
StateMachinePath: {
|
|
15677
16066
|
states: (value) => value.states,
|
|
@@ -15738,6 +16127,14 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15738
16127
|
name
|
|
15739
16128
|
from { name }
|
|
15740
16129
|
to { name }
|
|
16130
|
+
label
|
|
16131
|
+
description
|
|
16132
|
+
action_json
|
|
16133
|
+
assignee_json
|
|
16134
|
+
requirements_json
|
|
16135
|
+
permission_json
|
|
16136
|
+
risk
|
|
16137
|
+
expected_outcome_json
|
|
15741
16138
|
}
|
|
15742
16139
|
}`
|
|
15743
16140
|
]
|
|
@@ -16426,7 +16823,7 @@ ${effectMetamodelTable}
|
|
|
16426
16823
|
| \`enqueueRecordImport\`, \`listRecordImports\`, \`getRecordImport\`, \`getRecordImportSummary\`, \u2026 | **Async** bulk import (worker queue + aggregate progress). Prefer when loads are huge, returning an \`importId\` is enough up front, and background processing is acceptable. |
|
|
16427
16824
|
| \`graphql(query, variables?)\` | **GraphQL** \u2014 see dedicated subsection below. |
|
|
16428
16825
|
| \`defineRelationship\`, \`getRelationships\`, \`attach\`, \`detach\`, \`listRelated\` | Imperative relationship operations (same ideas as manifest \`defineRelationship\`). |
|
|
16429
|
-
| \`sessions.list()\`, \`sessions.create()\`, \`sessions.connect()\`, \`sessions.reopen()\`, \`sessions.close()\` |
|
|
16826
|
+
| \`sessions.list({ status?, sessionScope?, limit?, offset? })\`, \`sessions.create({ sessionScope? })\`, \`sessions.connect()\`, \`sessions.reopen()\`, \`sessions.close()\` | Scoped, bounded session history and lifecycle for this environment. |
|
|
16430
16827
|
|
|
16431
16828
|
### \`Session\` (live runtime connection)
|
|
16432
16829
|
|
|
@@ -16474,7 +16871,7 @@ Use \`environment.graphql(query, variables?)\` when you need **query/mutation ac
|
|
|
16474
16871
|
| \`granular simulate\` | Open simulator in browser. |
|
|
16475
16872
|
| \`granular simulate --print-url\` | Print a deep-linkable simulator URL without opening the browser. |
|
|
16476
16873
|
| \`granular connect test --json\` | Verify auth and environment connectivity with a real session. |
|
|
16477
|
-
| \`granular session create/list/heap/doc --json\` | Create, enumerate, and inspect real session state. |
|
|
16874
|
+
| \`granular session create/list/heap/doc --json\` | Create, enumerate, and inspect real session state. \`session list\` defaults to 25 rows; use \`--session-scope\`, \`--subject-id\`, \`--limit\`, and \`--offset\` for deterministic paging. |
|
|
16478
16875
|
| \`granular graphql --query '...' --json\` | Run a GraphQL query or mutation against the live graph. |
|
|
16479
16876
|
| \`granular effects list/diff --json\` | Inspect declared versus live ready effects. |
|
|
16480
16877
|
| \`granular job run --file ./job.ts --json\` | Execute a real runtime job from the terminal or CI. |
|
|
@@ -17154,8 +17551,8 @@ Use this for command execution, environment setup, and shipping flows.
|
|
|
17154
17551
|
| \`granular graphql --query '...' --json\` | Query the live graph through the environment GraphQL API |
|
|
17155
17552
|
| \`granular effects list --json\` | Inspect declared and live effects for an environment |
|
|
17156
17553
|
| \`granular effects diff --json\` | Compare declared effects to live ready handlers |
|
|
17157
|
-
| \`granular session create --json\` | Create a fresh session for an environment |
|
|
17158
|
-
| \`granular session list --json\` | List
|
|
17554
|
+
| \`granular session create --session-scope <scope> --json\` | Create a fresh, application-scoped session for an environment |
|
|
17555
|
+
| \`granular session list --session-scope <scope> --limit 25 --offset 0 --json\` | List one bounded page of indexed sessions |
|
|
17159
17556
|
| \`granular session heap --json\` | Inspect session heap |
|
|
17160
17557
|
| \`granular session doc --json\` | Inspect the Automerge-backed session document |
|
|
17161
17558
|
| \`granular job run --file ./job.ts\` | Execute a real job against the ontology runtime |
|
|
@@ -17238,8 +17635,8 @@ Use this for runtime debugging after the ontology builds but behavior does not m
|
|
|
17238
17635
|
|
|
17239
17636
|
| Goal | Preferred path |
|
|
17240
17637
|
| --- | --- |
|
|
17241
|
-
| Create or rotate a session | \`granular session create --json\` |
|
|
17242
|
-
| List known sessions | \`granular session list --json\` |
|
|
17638
|
+
| Create or rotate a session | \`granular session create --session-scope <scope> --json\` |
|
|
17639
|
+
| List known sessions | \`granular session list --session-scope <scope> --limit 25 --offset 0 --json\` |
|
|
17243
17640
|
| Inspect heap | \`granular session heap --json\` |
|
|
17244
17641
|
| Inspect full document | \`granular session doc --json\` |
|
|
17245
17642
|
| Verify connectivity | \`granular connect test\` |
|
|
@@ -19145,6 +19542,9 @@ function rpcTimeoutMsForMethod(method) {
|
|
|
19145
19542
|
return DOMAIN_PACKAGE_RPC_TIMEOUT_MS;
|
|
19146
19543
|
case "client.heartbeat":
|
|
19147
19544
|
case "effects.publishCatalog":
|
|
19545
|
+
case "effects.resetCatalog":
|
|
19546
|
+
case "effects.addCatalog":
|
|
19547
|
+
case "effects.removeCatalog":
|
|
19148
19548
|
case "effects.refresh":
|
|
19149
19549
|
return EFFECT_CONTROL_RPC_TIMEOUT_MS;
|
|
19150
19550
|
case "harness.run":
|
|
@@ -19169,16 +19569,37 @@ var WSClient = class {
|
|
|
19169
19569
|
tokenRefreshTimer = null;
|
|
19170
19570
|
isExplicitlyDisconnected = false;
|
|
19171
19571
|
reconnectAttempts = 0;
|
|
19572
|
+
connectPromise = null;
|
|
19573
|
+
connectionEpoch = 0;
|
|
19574
|
+
cancelConnectAttempt = null;
|
|
19172
19575
|
options;
|
|
19173
19576
|
constructor(options) {
|
|
19174
19577
|
this.options = options;
|
|
19175
19578
|
this.url = options.url;
|
|
19176
19579
|
this.sessionId = options.sessionId;
|
|
19177
19580
|
this.token = options.token;
|
|
19581
|
+
if (options.initialDocumentSnapshot) {
|
|
19582
|
+
this.seedDocumentSnapshot(options.initialDocumentSnapshot);
|
|
19583
|
+
}
|
|
19178
19584
|
}
|
|
19179
19585
|
get currentSessionId() {
|
|
19180
19586
|
return this.sessionId;
|
|
19181
19587
|
}
|
|
19588
|
+
seedDocumentSnapshot(document) {
|
|
19589
|
+
if (!document || typeof document !== "object" || Array.isArray(document)) {
|
|
19590
|
+
return;
|
|
19591
|
+
}
|
|
19592
|
+
try {
|
|
19593
|
+
this.doc = document instanceof Uint8Array ? Automerge__namespace.load(document) : Automerge__namespace.from(document);
|
|
19594
|
+
this.syncState = Automerge__namespace.initSyncState();
|
|
19595
|
+
this.emit("sync", this.doc);
|
|
19596
|
+
} catch (error2) {
|
|
19597
|
+
console.warn("[Granular] Failed to seed cached session document", error2);
|
|
19598
|
+
}
|
|
19599
|
+
}
|
|
19600
|
+
saveDocumentSnapshot() {
|
|
19601
|
+
return Automerge__namespace.save(this.doc);
|
|
19602
|
+
}
|
|
19182
19603
|
clearTokenRefreshTimer() {
|
|
19183
19604
|
if (this.tokenRefreshTimer) {
|
|
19184
19605
|
clearTimeout(this.tokenRefreshTimer);
|
|
@@ -19288,8 +19709,23 @@ var WSClient = class {
|
|
|
19288
19709
|
* Connect to the WebSocket server
|
|
19289
19710
|
* @returns {Promise<void>} Resolves when connection is open
|
|
19290
19711
|
*/
|
|
19291
|
-
async connect() {
|
|
19712
|
+
async connect(options = {}) {
|
|
19713
|
+
if (this.ws?.readyState === READY_STATE_OPEN) return;
|
|
19714
|
+
if (this.connectPromise) return this.connectPromise;
|
|
19715
|
+
const connectPromise = this.connectAttempt(options.signal);
|
|
19716
|
+
this.connectPromise = connectPromise;
|
|
19717
|
+
try {
|
|
19718
|
+
await connectPromise;
|
|
19719
|
+
} finally {
|
|
19720
|
+
if (this.connectPromise === connectPromise) {
|
|
19721
|
+
this.connectPromise = null;
|
|
19722
|
+
}
|
|
19723
|
+
}
|
|
19724
|
+
}
|
|
19725
|
+
async connectAttempt(signal) {
|
|
19726
|
+
if (signal?.aborted) throw new Error("WebSocket connect aborted");
|
|
19292
19727
|
const token = await this.resolveTokenForConnect();
|
|
19728
|
+
if (signal?.aborted) throw new Error("WebSocket connect aborted");
|
|
19293
19729
|
this.isExplicitlyDisconnected = false;
|
|
19294
19730
|
this.scheduleTokenRefresh();
|
|
19295
19731
|
if (this.reconnectTimer) {
|
|
@@ -19301,7 +19737,7 @@ var WSClient = class {
|
|
|
19301
19737
|
try {
|
|
19302
19738
|
const wsModule = await import('ws');
|
|
19303
19739
|
WebSocketClass = wsModule.default || wsModule;
|
|
19304
|
-
} catch
|
|
19740
|
+
} catch {
|
|
19305
19741
|
}
|
|
19306
19742
|
}
|
|
19307
19743
|
if (!WebSocketClass) {
|
|
@@ -19309,83 +19745,97 @@ var WSClient = class {
|
|
|
19309
19745
|
'No WebSocket implementation found. If using Node.js, please install "ws" and pass the constructor to the SDK options: { WebSocketCtor: WebSocket }.'
|
|
19310
19746
|
);
|
|
19311
19747
|
}
|
|
19748
|
+
const epoch = ++this.connectionEpoch;
|
|
19749
|
+
const wsUrl = new URL(this.url);
|
|
19750
|
+
wsUrl.searchParams.set("sessionId", this.sessionId);
|
|
19751
|
+
wsUrl.searchParams.set("token", token);
|
|
19752
|
+
const socket = new WebSocketClass(wsUrl.toString());
|
|
19753
|
+
this.ws = socket;
|
|
19312
19754
|
return new Promise((resolve2, reject) => {
|
|
19313
|
-
|
|
19314
|
-
|
|
19315
|
-
|
|
19316
|
-
|
|
19317
|
-
|
|
19318
|
-
if (
|
|
19319
|
-
|
|
19320
|
-
|
|
19321
|
-
|
|
19322
|
-
|
|
19323
|
-
|
|
19324
|
-
this.reconnectTimer = null;
|
|
19325
|
-
}
|
|
19326
|
-
this.reconnectAttempts = 0;
|
|
19327
|
-
this.emit("open", {});
|
|
19328
|
-
resolve2();
|
|
19329
|
-
});
|
|
19330
|
-
socket.on("message", (data) => {
|
|
19331
|
-
try {
|
|
19332
|
-
const message = JSON.parse(data.toString());
|
|
19333
|
-
this.handleMessage(message);
|
|
19334
|
-
} catch (error2) {
|
|
19335
|
-
console.error("[Granular] Failed to parse message:", error2);
|
|
19336
|
-
}
|
|
19337
|
-
});
|
|
19338
|
-
socket.on("error", (error2) => {
|
|
19339
|
-
this.emit("error", error2);
|
|
19340
|
-
if (socket.readyState !== READY_STATE_OPEN) {
|
|
19341
|
-
reject(error2);
|
|
19342
|
-
}
|
|
19343
|
-
});
|
|
19344
|
-
socket.on("close", (code, reason) => {
|
|
19345
|
-
this.handleDisconnect({
|
|
19346
|
-
code,
|
|
19347
|
-
reason: this.normalizeReason(reason),
|
|
19348
|
-
// ws does not provide wasClean on Node-style close callback
|
|
19349
|
-
wasClean: code === 1e3
|
|
19350
|
-
});
|
|
19351
|
-
});
|
|
19755
|
+
let settled = false;
|
|
19756
|
+
const isCurrent = () => this.connectionEpoch === epoch && this.ws === socket;
|
|
19757
|
+
const finish = (error2) => {
|
|
19758
|
+
if (settled) return;
|
|
19759
|
+
settled = true;
|
|
19760
|
+
if (this.cancelConnectAttempt === handleAbort) {
|
|
19761
|
+
this.cancelConnectAttempt = null;
|
|
19762
|
+
}
|
|
19763
|
+
signal?.removeEventListener("abort", handleAbort);
|
|
19764
|
+
if (error2) {
|
|
19765
|
+
reject(error2 instanceof Error ? error2 : new Error(String(error2)));
|
|
19352
19766
|
} else {
|
|
19353
|
-
|
|
19354
|
-
if (this.reconnectTimer) {
|
|
19355
|
-
clearTimeout(this.reconnectTimer);
|
|
19356
|
-
this.reconnectTimer = null;
|
|
19357
|
-
}
|
|
19358
|
-
this.reconnectAttempts = 0;
|
|
19359
|
-
this.emit("open", {});
|
|
19360
|
-
resolve2();
|
|
19361
|
-
};
|
|
19362
|
-
this.ws.onmessage = (event) => {
|
|
19363
|
-
try {
|
|
19364
|
-
const data = event.data;
|
|
19365
|
-
const message = JSON.parse(data.toString());
|
|
19366
|
-
this.handleMessage(message);
|
|
19367
|
-
} catch (error2) {
|
|
19368
|
-
console.error("[Granular] Failed to parse message:", error2);
|
|
19369
|
-
}
|
|
19370
|
-
};
|
|
19371
|
-
this.ws.onerror = (event) => {
|
|
19372
|
-
const error2 = new Error("WebSocket error");
|
|
19373
|
-
error2.event = event;
|
|
19374
|
-
this.emit("error", error2);
|
|
19375
|
-
if (this.ws?.readyState !== READY_STATE_OPEN) {
|
|
19376
|
-
reject(error2);
|
|
19377
|
-
}
|
|
19378
|
-
};
|
|
19379
|
-
this.ws.onclose = (event) => {
|
|
19380
|
-
this.handleDisconnect({
|
|
19381
|
-
code: event.code,
|
|
19382
|
-
reason: event.reason,
|
|
19383
|
-
wasClean: event.wasClean
|
|
19384
|
-
});
|
|
19385
|
-
};
|
|
19767
|
+
resolve2();
|
|
19386
19768
|
}
|
|
19387
|
-
}
|
|
19388
|
-
|
|
19769
|
+
};
|
|
19770
|
+
const closeStaleSocket = () => {
|
|
19771
|
+
try {
|
|
19772
|
+
socket.close(1e3, "Stale connection attempt");
|
|
19773
|
+
} catch {
|
|
19774
|
+
}
|
|
19775
|
+
};
|
|
19776
|
+
const handleAbort = () => {
|
|
19777
|
+
if (isCurrent()) {
|
|
19778
|
+
this.connectionEpoch += 1;
|
|
19779
|
+
this.ws = null;
|
|
19780
|
+
}
|
|
19781
|
+
closeStaleSocket();
|
|
19782
|
+
finish(new Error("WebSocket connect aborted"));
|
|
19783
|
+
};
|
|
19784
|
+
this.cancelConnectAttempt = handleAbort;
|
|
19785
|
+
const handleOpen = () => {
|
|
19786
|
+
if (!isCurrent()) {
|
|
19787
|
+
closeStaleSocket();
|
|
19788
|
+
return;
|
|
19789
|
+
}
|
|
19790
|
+
this.reconnectAttempts = 0;
|
|
19791
|
+
this.emit("open", {});
|
|
19792
|
+
finish();
|
|
19793
|
+
};
|
|
19794
|
+
const handleMessage = (data) => {
|
|
19795
|
+
if (!isCurrent()) return;
|
|
19796
|
+
try {
|
|
19797
|
+
const text = typeof data === "string" ? data : data && typeof data === "object" && "toString" in data ? String(data.toString()) : "";
|
|
19798
|
+
this.handleMessage(JSON.parse(text));
|
|
19799
|
+
} catch (error2) {
|
|
19800
|
+
console.error("[Granular] Failed to parse message:", error2);
|
|
19801
|
+
}
|
|
19802
|
+
};
|
|
19803
|
+
const handleError = (error2) => {
|
|
19804
|
+
if (!isCurrent()) return;
|
|
19805
|
+
const typedError = error2 instanceof Error ? error2 : new Error("WebSocket error");
|
|
19806
|
+
this.emit("error", typedError);
|
|
19807
|
+
if (socket.readyState !== READY_STATE_OPEN) finish(typedError);
|
|
19808
|
+
};
|
|
19809
|
+
const handleClose = (close) => {
|
|
19810
|
+
if (!isCurrent()) return;
|
|
19811
|
+
if (!settled) {
|
|
19812
|
+
finish(
|
|
19813
|
+
new Error(
|
|
19814
|
+
`WebSocket closed before ready${close.code ? ` (code=${close.code})` : ""}`
|
|
19815
|
+
)
|
|
19816
|
+
);
|
|
19817
|
+
}
|
|
19818
|
+
this.handleDisconnect({
|
|
19819
|
+
code: close.code,
|
|
19820
|
+
reason: this.normalizeReason(close.reason),
|
|
19821
|
+
wasClean: close.wasClean
|
|
19822
|
+
});
|
|
19823
|
+
};
|
|
19824
|
+
signal?.addEventListener("abort", handleAbort, { once: true });
|
|
19825
|
+
const nodeSocket = socket;
|
|
19826
|
+
if (typeof nodeSocket.on === "function") {
|
|
19827
|
+
nodeSocket.on("open", handleOpen);
|
|
19828
|
+
nodeSocket.on("message", handleMessage);
|
|
19829
|
+
nodeSocket.on("error", handleError);
|
|
19830
|
+
nodeSocket.on(
|
|
19831
|
+
"close",
|
|
19832
|
+
(code, reason) => handleClose({ code, reason, wasClean: code === 1e3 })
|
|
19833
|
+
);
|
|
19834
|
+
} else {
|
|
19835
|
+
socket.onopen = handleOpen;
|
|
19836
|
+
socket.onmessage = (event) => handleMessage(event.data);
|
|
19837
|
+
socket.onerror = handleError;
|
|
19838
|
+
socket.onclose = (event) => handleClose(event);
|
|
19389
19839
|
}
|
|
19390
19840
|
});
|
|
19391
19841
|
}
|
|
@@ -19403,9 +19853,58 @@ var WSClient = class {
|
|
|
19403
19853
|
return void 0;
|
|
19404
19854
|
}
|
|
19405
19855
|
rejectPending(error2) {
|
|
19406
|
-
this.messageQueue.forEach((pending) =>
|
|
19856
|
+
this.messageQueue.forEach((pending) => {
|
|
19857
|
+
clearTimeout(pending.timeout);
|
|
19858
|
+
pending.reject(error2);
|
|
19859
|
+
});
|
|
19407
19860
|
this.messageQueue = [];
|
|
19408
19861
|
}
|
|
19862
|
+
emitReconnectErrorMessage(error2) {
|
|
19863
|
+
const reconnectInfo = {
|
|
19864
|
+
error: error2,
|
|
19865
|
+
sessionId: this.sessionId,
|
|
19866
|
+
timestamp: Date.now()
|
|
19867
|
+
};
|
|
19868
|
+
this.emit("reconnect_error", reconnectInfo);
|
|
19869
|
+
if (this.options.onReconnectError) {
|
|
19870
|
+
try {
|
|
19871
|
+
this.options.onReconnectError(reconnectInfo);
|
|
19872
|
+
} catch (callbackError) {
|
|
19873
|
+
console.error(
|
|
19874
|
+
"[Granular] onReconnectError callback failed:",
|
|
19875
|
+
callbackError
|
|
19876
|
+
);
|
|
19877
|
+
}
|
|
19878
|
+
}
|
|
19879
|
+
}
|
|
19880
|
+
scheduleReconnectAttempt() {
|
|
19881
|
+
if (this.isExplicitlyDisconnected || this.reconnectTimer) return null;
|
|
19882
|
+
const baseReconnectDelayMs = typeof this.options.reconnectDelayMs === "number" && Number.isFinite(this.options.reconnectDelayMs) && this.options.reconnectDelayMs > 0 ? this.options.reconnectDelayMs : DEFAULT_RECONNECT_DELAY_MS;
|
|
19883
|
+
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;
|
|
19884
|
+
if (this.reconnectAttempts >= maxReconnectAttempts) {
|
|
19885
|
+
this.emitReconnectErrorMessage(
|
|
19886
|
+
`WebSocket reconnect attempts exhausted after ${maxReconnectAttempts} attempt(s).`
|
|
19887
|
+
);
|
|
19888
|
+
return null;
|
|
19889
|
+
}
|
|
19890
|
+
this.reconnectAttempts += 1;
|
|
19891
|
+
const reconnectDelayMs = Math.min(
|
|
19892
|
+
3e4,
|
|
19893
|
+
baseReconnectDelayMs * 2 ** Math.max(0, this.reconnectAttempts - 1)
|
|
19894
|
+
);
|
|
19895
|
+
this.reconnectTimer = setTimeout(() => {
|
|
19896
|
+
this.reconnectTimer = null;
|
|
19897
|
+
console.log("[Granular] Attempting reconnect...");
|
|
19898
|
+
this.connect().catch((error2) => {
|
|
19899
|
+
console.error("[Granular] Reconnect failed:", error2);
|
|
19900
|
+
this.emitReconnectErrorMessage(
|
|
19901
|
+
error2 instanceof Error ? error2.message : String(error2)
|
|
19902
|
+
);
|
|
19903
|
+
this.scheduleReconnectAttempt();
|
|
19904
|
+
});
|
|
19905
|
+
}, reconnectDelayMs);
|
|
19906
|
+
return reconnectDelayMs;
|
|
19907
|
+
}
|
|
19409
19908
|
buildDisconnectError(info2) {
|
|
19410
19909
|
const details = [
|
|
19411
19910
|
info2.code !== void 0 ? `code=${info2.code}` : void 0,
|
|
@@ -19415,8 +19914,6 @@ var WSClient = class {
|
|
|
19415
19914
|
return new Error(`WebSocket disconnected${suffix}`);
|
|
19416
19915
|
}
|
|
19417
19916
|
handleDisconnect(close = {}) {
|
|
19418
|
-
const baseReconnectDelayMs = typeof this.options.reconnectDelayMs === "number" && Number.isFinite(this.options.reconnectDelayMs) && this.options.reconnectDelayMs > 0 ? this.options.reconnectDelayMs : DEFAULT_RECONNECT_DELAY_MS;
|
|
19419
|
-
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;
|
|
19420
19917
|
const unexpected = !this.isExplicitlyDisconnected;
|
|
19421
19918
|
const info2 = {
|
|
19422
19919
|
code: close.code,
|
|
@@ -19436,32 +19933,9 @@ var WSClient = class {
|
|
|
19436
19933
|
const disconnectError = this.buildDisconnectError(info2);
|
|
19437
19934
|
this.rejectPending(disconnectError);
|
|
19438
19935
|
this.emit("disconnect", info2);
|
|
19439
|
-
|
|
19440
|
-
|
|
19441
|
-
|
|
19442
|
-
sessionId: this.sessionId,
|
|
19443
|
-
timestamp: Date.now()
|
|
19444
|
-
};
|
|
19445
|
-
this.emit("reconnect_error", reconnectInfo);
|
|
19446
|
-
if (this.options.onReconnectError) {
|
|
19447
|
-
try {
|
|
19448
|
-
this.options.onReconnectError(reconnectInfo);
|
|
19449
|
-
} catch (callbackError) {
|
|
19450
|
-
console.error(
|
|
19451
|
-
"[Granular] onReconnectError callback failed:",
|
|
19452
|
-
callbackError
|
|
19453
|
-
);
|
|
19454
|
-
}
|
|
19455
|
-
}
|
|
19456
|
-
return;
|
|
19457
|
-
}
|
|
19458
|
-
this.reconnectAttempts += 1;
|
|
19459
|
-
const reconnectDelayMs = Math.min(
|
|
19460
|
-
3e4,
|
|
19461
|
-
baseReconnectDelayMs * 2 ** Math.max(0, this.reconnectAttempts - 1)
|
|
19462
|
-
);
|
|
19463
|
-
info2.reconnectScheduled = true;
|
|
19464
|
-
info2.reconnectDelayMs = reconnectDelayMs;
|
|
19936
|
+
const reconnectDelayMs = this.scheduleReconnectAttempt();
|
|
19937
|
+
info2.reconnectScheduled = reconnectDelayMs !== null;
|
|
19938
|
+
if (reconnectDelayMs !== null) info2.reconnectDelayMs = reconnectDelayMs;
|
|
19465
19939
|
if (this.options.onUnexpectedClose) {
|
|
19466
19940
|
try {
|
|
19467
19941
|
this.options.onUnexpectedClose(info2);
|
|
@@ -19472,28 +19946,6 @@ var WSClient = class {
|
|
|
19472
19946
|
);
|
|
19473
19947
|
}
|
|
19474
19948
|
}
|
|
19475
|
-
this.reconnectTimer = setTimeout(() => {
|
|
19476
|
-
console.log("[Granular] Attempting reconnect...");
|
|
19477
|
-
this.connect().catch((error2) => {
|
|
19478
|
-
console.error("[Granular] Reconnect failed:", error2);
|
|
19479
|
-
const reconnectInfo = {
|
|
19480
|
-
error: error2 instanceof Error ? error2.message : String(error2),
|
|
19481
|
-
sessionId: this.sessionId,
|
|
19482
|
-
timestamp: Date.now()
|
|
19483
|
-
};
|
|
19484
|
-
this.emit("reconnect_error", reconnectInfo);
|
|
19485
|
-
if (this.options.onReconnectError) {
|
|
19486
|
-
try {
|
|
19487
|
-
this.options.onReconnectError(reconnectInfo);
|
|
19488
|
-
} catch (callbackError) {
|
|
19489
|
-
console.error(
|
|
19490
|
-
"[Granular] onReconnectError callback failed:",
|
|
19491
|
-
callbackError
|
|
19492
|
-
);
|
|
19493
|
-
}
|
|
19494
|
-
}
|
|
19495
|
-
});
|
|
19496
|
-
}, reconnectDelayMs);
|
|
19497
19949
|
}
|
|
19498
19950
|
}
|
|
19499
19951
|
handleMessage(message) {
|
|
@@ -19604,6 +20056,7 @@ var WSClient = class {
|
|
|
19604
20056
|
const response = message;
|
|
19605
20057
|
const pending = this.messageQueue.find((q) => q.id === response.id);
|
|
19606
20058
|
if (pending) {
|
|
20059
|
+
clearTimeout(pending.timeout);
|
|
19607
20060
|
if (response.type === "rpc_error") {
|
|
19608
20061
|
pending.reject(
|
|
19609
20062
|
new Error(
|
|
@@ -19649,16 +20102,22 @@ var WSClient = class {
|
|
|
19649
20102
|
id
|
|
19650
20103
|
};
|
|
19651
20104
|
return new Promise((resolve2, reject) => {
|
|
19652
|
-
this.messageQueue.push({ resolve: resolve2, reject, id });
|
|
19653
|
-
this.ws.send(JSON.stringify(request));
|
|
19654
20105
|
const timeoutMs = rpcTimeoutMsForMethod(method);
|
|
19655
|
-
setTimeout(() => {
|
|
20106
|
+
const timeout = setTimeout(() => {
|
|
19656
20107
|
const pending = this.messageQueue.find((q) => q.id === id);
|
|
19657
20108
|
if (pending) {
|
|
19658
20109
|
this.messageQueue = this.messageQueue.filter((q) => q.id !== id);
|
|
19659
20110
|
reject(new Error(`RPC timeout: ${method}`));
|
|
19660
20111
|
}
|
|
19661
20112
|
}, timeoutMs);
|
|
20113
|
+
this.messageQueue.push({ resolve: resolve2, reject, id, timeout });
|
|
20114
|
+
try {
|
|
20115
|
+
this.ws.send(JSON.stringify(request));
|
|
20116
|
+
} catch (error2) {
|
|
20117
|
+
clearTimeout(timeout);
|
|
20118
|
+
this.messageQueue = this.messageQueue.filter((q) => q.id !== id);
|
|
20119
|
+
reject(error2 instanceof Error ? error2 : new Error(String(error2)));
|
|
20120
|
+
}
|
|
19662
20121
|
});
|
|
19663
20122
|
}
|
|
19664
20123
|
async handleIncomingRpc(request) {
|
|
@@ -19744,15 +20203,18 @@ var WSClient = class {
|
|
|
19744
20203
|
/**
|
|
19745
20204
|
* Disconnect the WebSocket and clear state
|
|
19746
20205
|
*/
|
|
19747
|
-
disconnect() {
|
|
20206
|
+
disconnect(options = {}) {
|
|
19748
20207
|
this.isExplicitlyDisconnected = true;
|
|
20208
|
+
this.cancelConnectAttempt?.();
|
|
20209
|
+
this.cancelConnectAttempt = null;
|
|
20210
|
+
this.connectionEpoch += 1;
|
|
19749
20211
|
if (this.reconnectTimer) {
|
|
19750
20212
|
clearTimeout(this.reconnectTimer);
|
|
19751
20213
|
this.reconnectTimer = null;
|
|
19752
20214
|
}
|
|
19753
20215
|
this.clearTokenRefreshTimer();
|
|
19754
20216
|
if (this.ws) {
|
|
19755
|
-
this.ws.close(1e3, "Client disconnect");
|
|
20217
|
+
this.ws.close(1e3, options.reason || "Client disconnect");
|
|
19756
20218
|
this.ws = null;
|
|
19757
20219
|
}
|
|
19758
20220
|
this.rejectPending(new Error("Client explicitly disconnected"));
|
|
@@ -19848,8 +20310,12 @@ function normalizePrompt(rawValue) {
|
|
|
19848
20310
|
const source = promptRecord || raw;
|
|
19849
20311
|
const id = typeof source.id === "string" ? source.id : typeof raw.id === "string" ? raw.id : typeof raw.promptId === "string" ? raw.promptId : "";
|
|
19850
20312
|
if (!id) return null;
|
|
20313
|
+
const jobId = typeof source.jobId === "string" && source.jobId.trim() ? source.jobId.trim() : typeof raw.jobId === "string" && raw.jobId.trim() ? raw.jobId.trim() : void 0;
|
|
20314
|
+
const turnId = typeof source.turnId === "string" && source.turnId.trim() ? source.turnId.trim() : typeof raw.turnId === "string" && raw.turnId.trim() ? raw.turnId.trim() : void 0;
|
|
19851
20315
|
return {
|
|
19852
20316
|
id,
|
|
20317
|
+
...jobId ? { jobId } : {},
|
|
20318
|
+
...turnId ? { turnId } : {},
|
|
19853
20319
|
type: normalizePromptType(source === raw ? raw : { ...raw, ...source }),
|
|
19854
20320
|
title: typeof source.title === "string" ? source.title : "Input required",
|
|
19855
20321
|
message: typeof source.message === "string" ? source.message : "",
|
|
@@ -19890,6 +20356,9 @@ function resolvePromptAnswer(prompt3, answer) {
|
|
|
19890
20356
|
|
|
19891
20357
|
// src/session.ts
|
|
19892
20358
|
var PROMPT_TRANSCRIPT_APPEND_TIMEOUT_MS = 5e3;
|
|
20359
|
+
function toPascalCase3(value) {
|
|
20360
|
+
return value.split(/[_:\-\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
20361
|
+
}
|
|
19893
20362
|
function withPromptTranscriptTimeout(promise) {
|
|
19894
20363
|
let timeout = null;
|
|
19895
20364
|
return Promise.race([
|
|
@@ -19933,6 +20402,9 @@ var Session = class {
|
|
|
19933
20402
|
this.initialQuota = options.initialQuota || null;
|
|
19934
20403
|
this.setupEventHandlers();
|
|
19935
20404
|
this.setupToolInvokeHandler();
|
|
20405
|
+
this.currentDomainRevision = this.extractDomainRevisionFromDoc(
|
|
20406
|
+
this.client.doc
|
|
20407
|
+
);
|
|
19936
20408
|
}
|
|
19937
20409
|
extractDomainRevisionFromDoc(doc) {
|
|
19938
20410
|
const domain = doc?.domain;
|
|
@@ -20452,9 +20924,7 @@ var Session = class {
|
|
|
20452
20924
|
if (classes && Object.keys(classes).length > 0) {
|
|
20453
20925
|
let docs2 = "# Domain Documentation\n\n";
|
|
20454
20926
|
docs2 += "Import concrete classes from `@granular/domain/<Class>` and global backend actions from `@granular/actions/backend`:\n\n";
|
|
20455
|
-
const classNames = Object.keys(classes).map(
|
|
20456
|
-
(c) => c.charAt(0).toUpperCase() + c.slice(1)
|
|
20457
|
-
);
|
|
20927
|
+
const classNames = Object.keys(classes).map(toPascalCase3);
|
|
20458
20928
|
const globalNames = (globalTools || []).map((t) => t.name);
|
|
20459
20929
|
const importLines = [
|
|
20460
20930
|
...classNames.map(
|
|
@@ -20468,7 +20938,7 @@ ${importLines.join("\n") || "// No generated domain imports available."}
|
|
|
20468
20938
|
|
|
20469
20939
|
`;
|
|
20470
20940
|
for (const [className, cls] of Object.entries(classes)) {
|
|
20471
|
-
const TsName =
|
|
20941
|
+
const TsName = toPascalCase3(className);
|
|
20472
20942
|
docs2 += `## ${TsName}
|
|
20473
20943
|
|
|
20474
20944
|
`;
|
|
@@ -20840,6 +21310,7 @@ function normalizeJobAgentMessageEnvelope(data) {
|
|
|
20840
21310
|
}
|
|
20841
21311
|
return {
|
|
20842
21312
|
jobId: d.jobId,
|
|
21313
|
+
...typeof d.turnId === "string" && d.turnId.trim() ? { turnId: d.turnId.trim() } : {},
|
|
20843
21314
|
message: {
|
|
20844
21315
|
messageId: d.messageId,
|
|
20845
21316
|
kind: d.kind === "artifacts" ? "artifacts" : "text",
|
|
@@ -21458,6 +21929,28 @@ function asString(value) {
|
|
|
21458
21929
|
function trimString(value) {
|
|
21459
21930
|
return typeof value === "string" ? value.trim() : "";
|
|
21460
21931
|
}
|
|
21932
|
+
function compactJson(value, maxLength = 320) {
|
|
21933
|
+
if (value === void 0 || value === null) return void 0;
|
|
21934
|
+
try {
|
|
21935
|
+
const json = JSON.stringify(value);
|
|
21936
|
+
if (!json || json === "undefined") return void 0;
|
|
21937
|
+
return json.length > maxLength ? `${json.slice(0, maxLength)}...` : json;
|
|
21938
|
+
} catch {
|
|
21939
|
+
return String(value);
|
|
21940
|
+
}
|
|
21941
|
+
}
|
|
21942
|
+
function artifactRecordsById(liveDoc) {
|
|
21943
|
+
const artifacts = asRecord3(liveDoc?.artifacts);
|
|
21944
|
+
const byId = asRecord3(artifacts?.byId) || {};
|
|
21945
|
+
return Object.fromEntries(
|
|
21946
|
+
Object.entries(byId).map(([artifactId, value]) => {
|
|
21947
|
+
const record = asRecord3(value);
|
|
21948
|
+
return record ? [artifactId, record] : null;
|
|
21949
|
+
}).filter(
|
|
21950
|
+
(entry) => Boolean(entry)
|
|
21951
|
+
)
|
|
21952
|
+
);
|
|
21953
|
+
}
|
|
21461
21954
|
function normalizeShowRefs(value) {
|
|
21462
21955
|
const record = asRecord3(value);
|
|
21463
21956
|
if (!record) return void 0;
|
|
@@ -21474,9 +21967,106 @@ function normalizeShowRefs(value) {
|
|
|
21474
21967
|
entryPaths: normalizeRefs(record.entryPaths),
|
|
21475
21968
|
listNames: normalizeRefs(record.listNames),
|
|
21476
21969
|
variableNames: normalizeRefs(record.variableNames),
|
|
21477
|
-
fileIds: normalizeRefs(record.fileIds)
|
|
21970
|
+
fileIds: normalizeRefs(record.fileIds),
|
|
21971
|
+
sessionArtifactIds: normalizeRefs(record.sessionArtifactIds),
|
|
21972
|
+
actionSuggestions: normalizeActionSuggestions(record.actionSuggestions),
|
|
21973
|
+
tables: Array.isArray(record.tables) ? record.tables.filter(
|
|
21974
|
+
(table2) => Boolean(
|
|
21975
|
+
table2 && typeof table2 === "object" && !Array.isArray(table2) && Array.isArray(table2.columns) && Array.isArray(table2.rows)
|
|
21976
|
+
)
|
|
21977
|
+
) : void 0
|
|
21478
21978
|
};
|
|
21479
|
-
return show.entryPaths || show.listNames || show.variableNames || show.fileIds ? show : void 0;
|
|
21979
|
+
return show.entryPaths || show.listNames || show.variableNames || show.fileIds || show.sessionArtifactIds || show.actionSuggestions || show.tables ? show : void 0;
|
|
21980
|
+
}
|
|
21981
|
+
function normalizeActionSuggestions(value) {
|
|
21982
|
+
if (!Array.isArray(value)) return void 0;
|
|
21983
|
+
const suggestions = [];
|
|
21984
|
+
for (const item of value) {
|
|
21985
|
+
const record = asRecord3(item);
|
|
21986
|
+
if (!record) continue;
|
|
21987
|
+
const label = trimString(record.label);
|
|
21988
|
+
if (!label) continue;
|
|
21989
|
+
const suggestionId = trimString(record.suggestionId) || trimString(record.id) || label;
|
|
21990
|
+
suggestions.push({
|
|
21991
|
+
suggestionId,
|
|
21992
|
+
label,
|
|
21993
|
+
...typeof record.description === "string" ? { description: record.description } : {},
|
|
21994
|
+
...asRecord3(record.artifact) ? { artifact: asRecord3(record.artifact) } : {},
|
|
21995
|
+
...asRecord3(record.target) ? { target: asRecord3(record.target) } : {},
|
|
21996
|
+
...asRecord3(record.metadata) ? { metadata: asRecord3(record.metadata) } : {}
|
|
21997
|
+
});
|
|
21998
|
+
}
|
|
21999
|
+
return suggestions.length ? suggestions : void 0;
|
|
22000
|
+
}
|
|
22001
|
+
var TRANSCRIPT_MESSAGE_PART_LIMIT = 128;
|
|
22002
|
+
var TRANSCRIPT_MESSAGE_PART_TEXT_LIMIT = 2e5;
|
|
22003
|
+
var TRANSCRIPT_MESSAGE_PART_ACTION_LABEL_LIMIT = 1e3;
|
|
22004
|
+
var TRANSCRIPT_MESSAGE_ACTION_LIMIT = 64;
|
|
22005
|
+
function normalizeConversationMessageActions(value) {
|
|
22006
|
+
if (!Array.isArray(value) || value.length === 0) return void 0;
|
|
22007
|
+
const actions = [];
|
|
22008
|
+
for (const item of value.slice(0, TRANSCRIPT_MESSAGE_ACTION_LIMIT)) {
|
|
22009
|
+
const record = asRecord3(item);
|
|
22010
|
+
const kind = record?.kind;
|
|
22011
|
+
const label = trimString(record?.label ?? record?.title);
|
|
22012
|
+
const status = record?.status;
|
|
22013
|
+
if (kind !== "frontend" && kind !== "backend" && kind !== "system" || !label || label.length > TRANSCRIPT_MESSAGE_PART_ACTION_LABEL_LIMIT) {
|
|
22014
|
+
continue;
|
|
22015
|
+
}
|
|
22016
|
+
actions.push({
|
|
22017
|
+
kind,
|
|
22018
|
+
label,
|
|
22019
|
+
...status === "done" || status === "queued" || status === "failed" ? { status } : {}
|
|
22020
|
+
});
|
|
22021
|
+
}
|
|
22022
|
+
return actions.length ? actions : void 0;
|
|
22023
|
+
}
|
|
22024
|
+
function normalizeConversationMessageParts(value, canonicalContent, canonicalActions) {
|
|
22025
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > TRANSCRIPT_MESSAGE_PART_LIMIT) {
|
|
22026
|
+
return void 0;
|
|
22027
|
+
}
|
|
22028
|
+
const parts = [];
|
|
22029
|
+
const canonicalActionsById = new Map(
|
|
22030
|
+
(canonicalActions || []).map((action) => [
|
|
22031
|
+
`${action.kind}:${action.label}`,
|
|
22032
|
+
action
|
|
22033
|
+
])
|
|
22034
|
+
);
|
|
22035
|
+
const seenActionIds = /* @__PURE__ */ new Set();
|
|
22036
|
+
let textLength = 0;
|
|
22037
|
+
for (const item of value) {
|
|
22038
|
+
const record = asRecord3(item);
|
|
22039
|
+
if (!record) return void 0;
|
|
22040
|
+
if (record.type === "text") {
|
|
22041
|
+
if (typeof record.text !== "string" || record.text.length === 0) {
|
|
22042
|
+
return void 0;
|
|
22043
|
+
}
|
|
22044
|
+
textLength += record.text.length;
|
|
22045
|
+
if (textLength > TRANSCRIPT_MESSAGE_PART_TEXT_LIMIT) return void 0;
|
|
22046
|
+
parts.push({ type: "text", text: record.text });
|
|
22047
|
+
continue;
|
|
22048
|
+
}
|
|
22049
|
+
if (record.type !== "action") return void 0;
|
|
22050
|
+
const action = asRecord3(record.action);
|
|
22051
|
+
const kind = action?.kind;
|
|
22052
|
+
const label = trimString(action?.label);
|
|
22053
|
+
if (kind !== "frontend" && kind !== "backend" && kind !== "system" || !label || label.length > TRANSCRIPT_MESSAGE_PART_ACTION_LABEL_LIMIT) {
|
|
22054
|
+
return void 0;
|
|
22055
|
+
}
|
|
22056
|
+
const actionId = `${kind}:${label}`;
|
|
22057
|
+
const canonicalAction = canonicalActionsById.get(actionId);
|
|
22058
|
+
if (!canonicalAction) return void 0;
|
|
22059
|
+
if (seenActionIds.has(actionId)) continue;
|
|
22060
|
+
seenActionIds.add(actionId);
|
|
22061
|
+
parts.push({
|
|
22062
|
+
type: "action",
|
|
22063
|
+
action: canonicalAction
|
|
22064
|
+
});
|
|
22065
|
+
}
|
|
22066
|
+
const orderedText = parts.filter(
|
|
22067
|
+
(part) => part.type === "text"
|
|
22068
|
+
).map((part) => part.text).join("");
|
|
22069
|
+
return orderedText === canonicalContent ? parts : void 0;
|
|
21480
22070
|
}
|
|
21481
22071
|
function stringifyTranscriptValue(value, fallback2 = "") {
|
|
21482
22072
|
if (typeof value === "string") {
|
|
@@ -21496,12 +22086,139 @@ function stringifyTranscriptValue(value, fallback2 = "") {
|
|
|
21496
22086
|
return String(value);
|
|
21497
22087
|
}
|
|
21498
22088
|
}
|
|
21499
|
-
function
|
|
22089
|
+
function latestInputEditSummary(metadata) {
|
|
22090
|
+
const lastInputEdit = asRecord3(metadata.lastInputEdit);
|
|
22091
|
+
if (!lastInputEdit) return null;
|
|
22092
|
+
const source = asString(lastInputEdit.source) || "unknown";
|
|
22093
|
+
const actor = asString(lastInputEdit.actorSubjectId) || asString(lastInputEdit.actorPermissionProfileName) || asString(lastInputEdit.jobId) || null;
|
|
22094
|
+
const inputKeys = Array.isArray(lastInputEdit.changedInputKeys) ? lastInputEdit.changedInputKeys.filter(
|
|
22095
|
+
(key) => typeof key === "string" && key.trim().length > 0
|
|
22096
|
+
).slice(0, 6) : [];
|
|
22097
|
+
const relationshipKeys = Array.isArray(lastInputEdit.changedRelationshipKeys) ? lastInputEdit.changedRelationshipKeys.filter(
|
|
22098
|
+
(key) => typeof key === "string" && key.trim().length > 0
|
|
22099
|
+
).slice(0, 6) : [];
|
|
22100
|
+
const changed = [
|
|
22101
|
+
inputKeys.length ? `inputs=${inputKeys.join(",")}` : null,
|
|
22102
|
+
relationshipKeys.length ? `relationships=${relationshipKeys.join(",")}` : null
|
|
22103
|
+
].filter(Boolean);
|
|
22104
|
+
return `lastEdit=${source}${actor ? ` by ${actor}` : ""}${changed.length ? ` (${changed.join("; ")})` : ""}`;
|
|
22105
|
+
}
|
|
22106
|
+
function artifactIssueSummary(record) {
|
|
22107
|
+
const validation = asRecord3(record.validation);
|
|
22108
|
+
if (!validation) return null;
|
|
22109
|
+
const issues = Array.isArray(validation.issues) ? validation.issues.map((issue) => asRecord3(issue)).filter((issue) => Boolean(issue)).slice(0, 3) : [];
|
|
22110
|
+
if (issues.length > 0) {
|
|
22111
|
+
return `issues=${issues.map((issue) => {
|
|
22112
|
+
const code = asString(issue.code) || asString(issue.kind) || "issue";
|
|
22113
|
+
const path7 = asString(issue.path);
|
|
22114
|
+
const message = trimString(issue.message);
|
|
22115
|
+
return `${code}${path7 ? ` at ${path7}` : ""}${message ? ` (${message})` : ""}`;
|
|
22116
|
+
}).join("; ")}`;
|
|
22117
|
+
}
|
|
22118
|
+
const error2 = trimString(validation.error) || trimString(validation.reason) || trimString(validation.message);
|
|
22119
|
+
return error2 ? `validation=${error2}` : null;
|
|
22120
|
+
}
|
|
22121
|
+
function artifactExecutionSummary(metadata) {
|
|
22122
|
+
const execution = asRecord3(metadata.execution);
|
|
22123
|
+
if (!execution) return null;
|
|
22124
|
+
const result = asRecord3(execution.result);
|
|
22125
|
+
const awaiting = asString(result?.awaiting) || asString(execution.awaiting);
|
|
22126
|
+
const pendingTransition = asString(result?.pendingTransition) || asString(execution.pendingTransition);
|
|
22127
|
+
const approval = asRecord3(result?.approval) || asRecord3(execution.approval);
|
|
22128
|
+
const approvalTarget = asString(approval?.permissionProfileName) || asString(approval?.permissionProfileId) || asString(approval?.assigneeSubjectId);
|
|
22129
|
+
const error2 = trimString(execution.error);
|
|
22130
|
+
const pieces = [
|
|
22131
|
+
awaiting ? `awaiting=${awaiting}` : null,
|
|
22132
|
+
pendingTransition ? `pendingTransition=${pendingTransition}` : null,
|
|
22133
|
+
approvalTarget ? `approvalTarget=${approvalTarget}` : null,
|
|
22134
|
+
error2 ? `executionError=${error2}` : null
|
|
22135
|
+
].filter(Boolean);
|
|
22136
|
+
return pieces.length ? pieces.join("; ") : null;
|
|
22137
|
+
}
|
|
22138
|
+
function artifactStatePathSummary(metadata) {
|
|
22139
|
+
const statePlan = asRecord3(metadata.statePlan);
|
|
22140
|
+
if (!statePlan) return null;
|
|
22141
|
+
const machineName = asString(statePlan.machineName);
|
|
22142
|
+
const targetState = asString(statePlan.targetState);
|
|
22143
|
+
const objectPath = asString(statePlan.objectPath);
|
|
22144
|
+
const approvedTransitions = Array.isArray(statePlan.approvedTransitions) ? statePlan.approvedTransitions.length : 0;
|
|
22145
|
+
const approvalDecisions = Array.isArray(statePlan.approvalDecisions) ? statePlan.approvalDecisions.length : 0;
|
|
22146
|
+
const pieces = [
|
|
22147
|
+
machineName || targetState ? `statePath=${machineName || "state_machine"}${targetState ? ` -> ${targetState}` : ""}` : null,
|
|
22148
|
+
objectPath ? `objectPath=${objectPath}` : null,
|
|
22149
|
+
approvedTransitions ? `approvedTransitions=${approvedTransitions}` : null,
|
|
22150
|
+
approvalDecisions ? `approvalDecisions=${approvalDecisions}` : null
|
|
22151
|
+
].filter(Boolean);
|
|
22152
|
+
return pieces.length ? pieces.join("; ") : null;
|
|
22153
|
+
}
|
|
22154
|
+
function artifactSummaryLine(artifactId, record) {
|
|
22155
|
+
if (!record) return `- ${artifactId}: unavailable in session artifact store`;
|
|
22156
|
+
const label = trimString(record.label) || artifactId;
|
|
22157
|
+
const kind = asString(record.kind) || "artifact";
|
|
22158
|
+
const status = asString(record.status) || "unknown";
|
|
22159
|
+
const createdByJobId = asString(record.createdByJobId);
|
|
22160
|
+
const target = asRecord3(record.target);
|
|
22161
|
+
const metadata = asRecord3(record.metadata) || {};
|
|
22162
|
+
const subArtifactIds = Array.isArray(record.subArtifactIds) ? record.subArtifactIds.filter(
|
|
22163
|
+
(id) => typeof id === "string" && id.trim().length > 0
|
|
22164
|
+
).slice(0, 8) : [];
|
|
22165
|
+
const relationships = compactJson(record.relationships, 220);
|
|
22166
|
+
const pieces = [
|
|
22167
|
+
`kind=${kind}`,
|
|
22168
|
+
`status=${status}`,
|
|
22169
|
+
createdByJobId ? `createdByJob=${createdByJobId}` : null,
|
|
22170
|
+
target ? `target=${asString(target.className) || "record"}:${asString(target.id) || "unknown"}${asString(target.label) ? ` (${asString(target.label)})` : ""}` : null,
|
|
22171
|
+
artifactStatePathSummary(metadata),
|
|
22172
|
+
artifactExecutionSummary(metadata),
|
|
22173
|
+
artifactIssueSummary(record),
|
|
22174
|
+
latestInputEditSummary(metadata),
|
|
22175
|
+
subArtifactIds.length ? `subArtifacts=${subArtifactIds.join(",")}` : null,
|
|
22176
|
+
relationships ? `relationships=${relationships}` : null
|
|
22177
|
+
].filter(Boolean);
|
|
22178
|
+
return `- ${artifactId}: ${label}${pieces.length ? `; ${pieces.join("; ")}` : ""}`;
|
|
22179
|
+
}
|
|
22180
|
+
function buildArtifactHistory(show, artifactsById) {
|
|
21500
22181
|
if (!show) return void 0;
|
|
21501
|
-
|
|
22182
|
+
const artifactIds = show.sessionArtifactIds || [];
|
|
22183
|
+
const actionSuggestions = show.actionSuggestions || [];
|
|
22184
|
+
if (artifactIds.length === 0 && actionSuggestions.length === 0) {
|
|
22185
|
+
return `[Agent message]
|
|
21502
22186
|
${stringifyTranscriptValue({ show }, "")}`;
|
|
22187
|
+
}
|
|
22188
|
+
const lines = artifactIds.slice(0, 8).map(
|
|
22189
|
+
(artifactId) => artifactSummaryLine(artifactId, artifactsById?.[artifactId])
|
|
22190
|
+
);
|
|
22191
|
+
if (artifactIds.length > 8) {
|
|
22192
|
+
lines.push(`- ${artifactIds.length - 8} more artifacts omitted`);
|
|
22193
|
+
}
|
|
22194
|
+
if (actionSuggestions.length > 0) {
|
|
22195
|
+
if (artifactIds.length > 0) lines.push("[Agent suggested actions]");
|
|
22196
|
+
for (const suggestion of actionSuggestions.slice(0, 8)) {
|
|
22197
|
+
lines.push(
|
|
22198
|
+
`- ${suggestion.label}${suggestion.description ? `; ${suggestion.description}` : ""}`
|
|
22199
|
+
);
|
|
22200
|
+
}
|
|
22201
|
+
if (actionSuggestions.length > 8) {
|
|
22202
|
+
lines.push(`- ${actionSuggestions.length - 8} more suggestions omitted`);
|
|
22203
|
+
}
|
|
22204
|
+
}
|
|
22205
|
+
const otherRefs = {
|
|
22206
|
+
entryPaths: show.entryPaths,
|
|
22207
|
+
listNames: show.listNames,
|
|
22208
|
+
variableNames: show.variableNames,
|
|
22209
|
+
fileIds: show.fileIds
|
|
22210
|
+
};
|
|
22211
|
+
const hasOtherRefs = Object.values(otherRefs).some(
|
|
22212
|
+
(value) => Array.isArray(value) && value.length > 0
|
|
22213
|
+
);
|
|
22214
|
+
const title = artifactIds.length > 0 ? "[Agent displayed session artifacts]" : "[Agent suggested actions]";
|
|
22215
|
+
return [
|
|
22216
|
+
title,
|
|
22217
|
+
...lines,
|
|
22218
|
+
hasOtherRefs ? `Other shown refs: ${stringifyTranscriptValue(otherRefs, "")}` : null
|
|
22219
|
+
].filter(Boolean).join("\n");
|
|
21503
22220
|
}
|
|
21504
|
-
function normalizeConversationMessage(raw) {
|
|
22221
|
+
function normalizeConversationMessage(raw, artifactsById) {
|
|
21505
22222
|
const record = asRecord3(raw);
|
|
21506
22223
|
if (!record) return null;
|
|
21507
22224
|
const role = record.role === "user" ? "user" : record.role === "assistant" ? "assistant" : null;
|
|
@@ -21509,10 +22226,18 @@ function normalizeConversationMessage(raw) {
|
|
|
21509
22226
|
const content = trimString(
|
|
21510
22227
|
record.content ?? record.reply ?? record.message ?? record.text
|
|
21511
22228
|
);
|
|
22229
|
+
const actions = role === "assistant" ? normalizeConversationMessageActions(record.actions) : void 0;
|
|
22230
|
+
const parts = role === "assistant" ? normalizeConversationMessageParts(record.parts, content, actions) : void 0;
|
|
21512
22231
|
const show = normalizeShowRefs(record.show);
|
|
21513
22232
|
const id = asString(record.id) || crypto.randomUUID();
|
|
21514
22233
|
const timestamp = asNumber(record.timestamp) || asNumber(record.ts) || 0;
|
|
21515
|
-
if (!content && !show) return null;
|
|
22234
|
+
if (!content && !show && !actions?.length) return null;
|
|
22235
|
+
const artifactHistory = buildArtifactHistory(show, artifactsById);
|
|
22236
|
+
const historyContent = role === "assistant" ? content && artifactHistory ? `[Assistant reply]
|
|
22237
|
+
${content}
|
|
22238
|
+
|
|
22239
|
+
${artifactHistory}` : content ? `[Assistant reply]
|
|
22240
|
+
${content}` : artifactHistory : void 0;
|
|
21516
22241
|
return {
|
|
21517
22242
|
id,
|
|
21518
22243
|
role,
|
|
@@ -21521,8 +22246,9 @@ function normalizeConversationMessage(raw) {
|
|
|
21521
22246
|
jobId: asString(record.jobId),
|
|
21522
22247
|
promptId: asString(record.promptId),
|
|
21523
22248
|
show,
|
|
21524
|
-
|
|
21525
|
-
|
|
22249
|
+
actions,
|
|
22250
|
+
parts,
|
|
22251
|
+
historyContent,
|
|
21526
22252
|
source: "conversation"
|
|
21527
22253
|
};
|
|
21528
22254
|
}
|
|
@@ -21565,7 +22291,7 @@ ${assistantContent}`,
|
|
|
21565
22291
|
return entries;
|
|
21566
22292
|
});
|
|
21567
22293
|
}
|
|
21568
|
-
function normalizeAgentMessageEntries(jobId, rawMessages) {
|
|
22294
|
+
function normalizeAgentMessageEntries(jobId, rawMessages, artifactsById) {
|
|
21569
22295
|
return asArray(rawMessages).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
|
|
21570
22296
|
(left, right) => (asNumber(left.timestamp) || asNumber(left.ts) || 0) - (asNumber(right.timestamp) || asNumber(right.ts) || 0)
|
|
21571
22297
|
).flatMap((message) => {
|
|
@@ -21596,14 +22322,14 @@ ${reply}`,
|
|
|
21596
22322
|
timestamp,
|
|
21597
22323
|
jobId,
|
|
21598
22324
|
show,
|
|
21599
|
-
historyContent: buildArtifactHistory(show),
|
|
22325
|
+
historyContent: buildArtifactHistory(show, artifactsById),
|
|
21600
22326
|
source: "job_agent_message"
|
|
21601
22327
|
});
|
|
21602
22328
|
}
|
|
21603
22329
|
return entries;
|
|
21604
22330
|
});
|
|
21605
22331
|
}
|
|
21606
|
-
function buildJobFallbackEntries(jobId, job2, sessionHeap) {
|
|
22332
|
+
function buildJobFallbackEntries(jobId, job2, sessionHeap, artifactsById) {
|
|
21607
22333
|
const timestamp = asNumber(job2.finishedAt) || asNumber(job2.startedAt) || asNumber(job2.submittedAt) || 0;
|
|
21608
22334
|
const resultPreview = stringifyTranscriptValue(
|
|
21609
22335
|
job2.result,
|
|
@@ -21641,7 +22367,7 @@ ${responseText}`,
|
|
|
21641
22367
|
timestamp,
|
|
21642
22368
|
jobId,
|
|
21643
22369
|
show,
|
|
21644
|
-
historyContent: buildArtifactHistory(show),
|
|
22370
|
+
historyContent: buildArtifactHistory(show, artifactsById),
|
|
21645
22371
|
source: "job_result"
|
|
21646
22372
|
});
|
|
21647
22373
|
}
|
|
@@ -21695,10 +22421,11 @@ function buildJobCodeEntry(jobId, job2) {
|
|
|
21695
22421
|
function buildSessionTranscript(input) {
|
|
21696
22422
|
const liveDoc = input.liveDoc || null;
|
|
21697
22423
|
const sessionHeap = input.sessionHeap || EMPTY_HEAP;
|
|
22424
|
+
const artifactsById = artifactRecordsById(liveDoc);
|
|
21698
22425
|
const transcript = [];
|
|
21699
22426
|
const conversationMessages = asArray(
|
|
21700
22427
|
asRecord3(liveDoc?.conversation)?.messages
|
|
21701
|
-
).map((message) => normalizeConversationMessage(message)).filter((message) => Boolean(message));
|
|
22428
|
+
).map((message) => normalizeConversationMessage(message, artifactsById)).filter((message) => Boolean(message));
|
|
21702
22429
|
const conversationPromptIds = new Set(
|
|
21703
22430
|
conversationMessages.map((message) => message.promptId).filter((promptId) => Boolean(promptId))
|
|
21704
22431
|
);
|
|
@@ -21725,7 +22452,8 @@ function buildSessionTranscript(input) {
|
|
|
21725
22452
|
if (!assistantConversationJobIds.has(jobId)) {
|
|
21726
22453
|
const agentEntries = normalizeAgentMessageEntries(
|
|
21727
22454
|
jobId,
|
|
21728
|
-
job2.agentMessages
|
|
22455
|
+
job2.agentMessages,
|
|
22456
|
+
artifactsById
|
|
21729
22457
|
);
|
|
21730
22458
|
if (agentEntries.length > 0) {
|
|
21731
22459
|
transcript.push(...agentEntries);
|
|
@@ -21734,7 +22462,8 @@ function buildSessionTranscript(input) {
|
|
|
21734
22462
|
...buildJobFallbackEntries(
|
|
21735
22463
|
jobId,
|
|
21736
22464
|
job2,
|
|
21737
|
-
sessionHeap
|
|
22465
|
+
sessionHeap,
|
|
22466
|
+
artifactsById
|
|
21738
22467
|
)
|
|
21739
22468
|
);
|
|
21740
22469
|
}
|
|
@@ -21982,15 +22711,50 @@ function toRecordSearchResult(className, node) {
|
|
|
21982
22711
|
return [];
|
|
21983
22712
|
}
|
|
21984
22713
|
) : [];
|
|
22714
|
+
const graphPathId = extractRecordIdFromGraphPath(path7, className);
|
|
22715
|
+
const realIdField = fields.find(
|
|
22716
|
+
(field) => normalizeGraphPathSegment(field.name) === "real_id" && typeof field.value === "string" && field.value.trim()
|
|
22717
|
+
);
|
|
22718
|
+
const id = typeof realIdField?.value === "string" ? realIdField.value.trim() : graphPathId;
|
|
22719
|
+
const rawLabel = typeof node.label === "string" && node.label.trim() ? node.label : "";
|
|
22720
|
+
if (fields.length === 0 && rawLabel && isPlaceholderRecordLabel(rawLabel, graphPathId, path7)) {
|
|
22721
|
+
return null;
|
|
22722
|
+
}
|
|
22723
|
+
const fallbackLabel = displayLabelFromFields(fields);
|
|
22724
|
+
const label = rawLabel && !isPlaceholderRecordLabel(rawLabel, id, path7) ? rawLabel : fallbackLabel || rawLabel || id;
|
|
21985
22725
|
return {
|
|
21986
22726
|
path: path7,
|
|
21987
22727
|
className,
|
|
21988
|
-
id
|
|
21989
|
-
label
|
|
22728
|
+
id,
|
|
22729
|
+
label,
|
|
21990
22730
|
description: typeof node.description === "string" && node.description.trim() ? node.description : null,
|
|
21991
22731
|
fields
|
|
21992
22732
|
};
|
|
21993
22733
|
}
|
|
22734
|
+
function isPlaceholderRecordLabel(label, id, path7) {
|
|
22735
|
+
const normalizedLabel = normalizeGraphPathSegment(label);
|
|
22736
|
+
return normalizedLabel === normalizeGraphPathSegment(id) || normalizedLabel === normalizeGraphPathSegment(path7);
|
|
22737
|
+
}
|
|
22738
|
+
function displayLabelFromFields(fields) {
|
|
22739
|
+
const preferredFieldNames = [
|
|
22740
|
+
"name",
|
|
22741
|
+
"title",
|
|
22742
|
+
"label",
|
|
22743
|
+
"display_name",
|
|
22744
|
+
"file_name",
|
|
22745
|
+
"number",
|
|
22746
|
+
"code"
|
|
22747
|
+
];
|
|
22748
|
+
for (const preferred of preferredFieldNames) {
|
|
22749
|
+
const match = fields.find(
|
|
22750
|
+
(field) => normalizeGraphPathSegment(field.name) === preferred && typeof field.value === "string" && field.value.trim()
|
|
22751
|
+
);
|
|
22752
|
+
if (typeof match?.value === "string") {
|
|
22753
|
+
return match.value.trim();
|
|
22754
|
+
}
|
|
22755
|
+
}
|
|
22756
|
+
return null;
|
|
22757
|
+
}
|
|
21994
22758
|
function normalizeRecordSearchText(value) {
|
|
21995
22759
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
|
|
21996
22760
|
}
|
|
@@ -22132,7 +22896,12 @@ async function recordOpenAIUsageSpend(options) {
|
|
|
22132
22896
|
const metadata = {
|
|
22133
22897
|
...options.metadata || {},
|
|
22134
22898
|
...options.usage.rawUsage !== void 0 ? { openaiUsage: options.usage.rawUsage } : {},
|
|
22135
|
-
usageContext: context
|
|
22899
|
+
usageContext: context,
|
|
22900
|
+
pricingContextTier: options.usage.pricingContextTier,
|
|
22901
|
+
cacheWritePricePerMillionMicros: options.usage.cacheWritePricePerMillionMicros,
|
|
22902
|
+
cacheWriteTokens: options.usage.cacheWriteTokens,
|
|
22903
|
+
cacheWriteCostMicros: options.usage.cacheWriteCostMicros,
|
|
22904
|
+
longContextThresholdTokens: options.usage.longContextThresholdTokens
|
|
22136
22905
|
};
|
|
22137
22906
|
const response = await fetch(
|
|
22138
22907
|
`${toGranularHttpBase(options.apiUrl)}/control/spend/events`,
|
|
@@ -22203,6 +22972,18 @@ function buildEffectMetamodelMutations(toolPath, spec) {
|
|
|
22203
22972
|
}
|
|
22204
22973
|
|
|
22205
22974
|
// src/client.ts
|
|
22975
|
+
var DEFAULT_CONVERSATION_SESSION_LIST_LIMIT = 100;
|
|
22976
|
+
var MAX_CONVERSATION_SESSION_LIST_LIMIT = 500;
|
|
22977
|
+
var MAX_CONVERSATION_SESSION_LIST_OFFSET = 1e5;
|
|
22978
|
+
function boundedSessionListInteger(value, name, fallback2, minimum, maximum) {
|
|
22979
|
+
if (value === void 0) return fallback2;
|
|
22980
|
+
if (!Number.isInteger(value) || value < minimum || value > maximum) {
|
|
22981
|
+
throw new RangeError(
|
|
22982
|
+
`Session list ${name} must be an integer between ${minimum} and ${maximum}.`
|
|
22983
|
+
);
|
|
22984
|
+
}
|
|
22985
|
+
return value;
|
|
22986
|
+
}
|
|
22206
22987
|
var STANDARD_MODULES_OPERATIONS = [
|
|
22207
22988
|
{
|
|
22208
22989
|
create: "entity",
|
|
@@ -22239,6 +23020,26 @@ var STANDARD_MODULES_OPERATIONS = [
|
|
|
22239
23020
|
var BUILTIN_MODULES = {
|
|
22240
23021
|
standard_modules: STANDARD_MODULES_OPERATIONS
|
|
22241
23022
|
};
|
|
23023
|
+
function stateNameFromMethodName(methodName) {
|
|
23024
|
+
const raw = methodName.startsWith("to") ? methodName.slice(2) : methodName;
|
|
23025
|
+
return raw.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
23026
|
+
}
|
|
23027
|
+
function appendQueryOptions(searchParams, query) {
|
|
23028
|
+
for (const [key, value] of Object.entries(query || {})) {
|
|
23029
|
+
if (value === null || typeof value === "undefined" || value === "") {
|
|
23030
|
+
continue;
|
|
23031
|
+
}
|
|
23032
|
+
if (value instanceof Date) {
|
|
23033
|
+
searchParams.set(key, value.toISOString());
|
|
23034
|
+
continue;
|
|
23035
|
+
}
|
|
23036
|
+
if (Array.isArray(value)) {
|
|
23037
|
+
if (value.length > 0) searchParams.set(key, value.join(","));
|
|
23038
|
+
continue;
|
|
23039
|
+
}
|
|
23040
|
+
searchParams.set(key, String(value));
|
|
23041
|
+
}
|
|
23042
|
+
}
|
|
22242
23043
|
var DEFAULT_DIRECT_RECORD_OBJECTS_REQUEST_BATCH_SIZE = 100;
|
|
22243
23044
|
var MAX_RECORD_OBJECTS_CONCURRENCY = 16;
|
|
22244
23045
|
var DEFAULT_DIRECT_RECORD_OBJECTS_RETRY_COUNT = 3;
|
|
@@ -22269,8 +23070,20 @@ function bodyInitFromSessionFileUpload(body) {
|
|
|
22269
23070
|
return body;
|
|
22270
23071
|
}
|
|
22271
23072
|
var EFFECT_CATALOG_SYNC_TIMEOUT_MS = 12e4;
|
|
23073
|
+
var EFFECT_CATALOG_SYNC_BATCH_SIZE = 20;
|
|
22272
23074
|
var EFFECT_CATALOG_SYNC_RETRY_COUNT = 3;
|
|
22273
23075
|
var EFFECT_CATALOG_SYNC_RETRY_DELAY_MS = 1e3;
|
|
23076
|
+
function chunkItems(items, batchSize) {
|
|
23077
|
+
const chunks = [];
|
|
23078
|
+
for (let offset = 0; offset < items.length; offset += batchSize) {
|
|
23079
|
+
chunks.push(items.slice(offset, offset + batchSize));
|
|
23080
|
+
}
|
|
23081
|
+
return chunks;
|
|
23082
|
+
}
|
|
23083
|
+
function isUnsupportedEffectCatalogMutation(error2) {
|
|
23084
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
23085
|
+
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");
|
|
23086
|
+
}
|
|
22274
23087
|
function planRecordObjectsChunks(records, batchSize) {
|
|
22275
23088
|
const total = records.length;
|
|
22276
23089
|
const size = Math.max(1, Math.min(batchSize, total));
|
|
@@ -22282,6 +23095,23 @@ function planRecordObjectsChunks(records, batchSize) {
|
|
|
22282
23095
|
}
|
|
22283
23096
|
return plans;
|
|
22284
23097
|
}
|
|
23098
|
+
function preserveRecordObjectRealId(record) {
|
|
23099
|
+
const realId = record.id.trim();
|
|
23100
|
+
if (!realId) {
|
|
23101
|
+
return record;
|
|
23102
|
+
}
|
|
23103
|
+
const fields = record.fields || {};
|
|
23104
|
+
if (typeof fields.real_id === "string" && fields.real_id.trim()) {
|
|
23105
|
+
return record;
|
|
23106
|
+
}
|
|
23107
|
+
return {
|
|
23108
|
+
...record,
|
|
23109
|
+
fields: {
|
|
23110
|
+
...fields,
|
|
23111
|
+
real_id: realId
|
|
23112
|
+
}
|
|
23113
|
+
};
|
|
23114
|
+
}
|
|
22285
23115
|
function computeEffectKey2(effect) {
|
|
22286
23116
|
const attachedClass = effect.className?.trim();
|
|
22287
23117
|
if (!attachedClass) {
|
|
@@ -22492,7 +23322,7 @@ var Environment = class _Environment {
|
|
|
22492
23322
|
}
|
|
22493
23323
|
get sessions() {
|
|
22494
23324
|
return {
|
|
22495
|
-
list: async (options) => this.listSessions(options
|
|
23325
|
+
list: async (options = {}) => this.listSessions(options),
|
|
22496
23326
|
create: async (options) => this.createSession(options),
|
|
22497
23327
|
connect: async (sessionId, options) => this.connectSession(sessionId, options),
|
|
22498
23328
|
reopen: async (sessionId, options) => this.reopenSession(sessionId, options),
|
|
@@ -22519,11 +23349,105 @@ var Environment = class _Environment {
|
|
|
22519
23349
|
getAwaitingCount: async () => this.getAwaitingRecordCount()
|
|
22520
23350
|
};
|
|
22521
23351
|
}
|
|
23352
|
+
/**
|
|
23353
|
+
* Mirror product-owned workflow state into Granular without making Granular
|
|
23354
|
+
* own the customer application's state machine.
|
|
23355
|
+
*/
|
|
23356
|
+
async recordState(input) {
|
|
23357
|
+
const { machine, state, ...target } = input;
|
|
23358
|
+
if (!machine.trim()) {
|
|
23359
|
+
throw new Error("State update requires a machine name");
|
|
23360
|
+
}
|
|
23361
|
+
if (!state.trim()) {
|
|
23362
|
+
throw new Error("State update requires a state");
|
|
23363
|
+
}
|
|
23364
|
+
return this.recordObject({
|
|
23365
|
+
className: target.className,
|
|
23366
|
+
id: target.id,
|
|
23367
|
+
...target.label ? { label: target.label } : {},
|
|
23368
|
+
...target.fields ? { fields: target.fields } : {},
|
|
23369
|
+
...target.relationships ? { relationships: target.relationships } : {},
|
|
23370
|
+
states: {
|
|
23371
|
+
[machine.trim()]: {
|
|
23372
|
+
state: state.trim(),
|
|
23373
|
+
...target.source ? { source: target.source } : {},
|
|
23374
|
+
...target.cause ? { cause: target.cause } : {},
|
|
23375
|
+
...target.actorId ? { actorId: target.actorId } : {},
|
|
23376
|
+
...target.observedAt !== void 0 ? { observedAt: target.observedAt } : {},
|
|
23377
|
+
...target.force !== void 0 ? { force: target.force } : {},
|
|
23378
|
+
...target.metadata ? { metadata: target.metadata } : {}
|
|
23379
|
+
}
|
|
23380
|
+
}
|
|
23381
|
+
});
|
|
23382
|
+
}
|
|
23383
|
+
/**
|
|
23384
|
+
* Mirror product-owned workflow state into Granular without making Granular
|
|
23385
|
+
* own the customer application's state machine.
|
|
23386
|
+
*
|
|
23387
|
+
* Example:
|
|
23388
|
+
* `await env.recordState({ className: "spend_request", id, machine: "lifecycle", state: "policy_review", source: "customer_backend" })`
|
|
23389
|
+
*/
|
|
23390
|
+
state(target) {
|
|
23391
|
+
const observe = async (machineName, stateName, input = {}) => {
|
|
23392
|
+
const observedState = input.observedState || input.state || stateName;
|
|
23393
|
+
if (!observedState) {
|
|
23394
|
+
throw new Error("State observation requires a target state");
|
|
23395
|
+
}
|
|
23396
|
+
return this.recordState({
|
|
23397
|
+
...target,
|
|
23398
|
+
machine: machineName,
|
|
23399
|
+
state: observedState,
|
|
23400
|
+
...input.source ? { source: input.source } : {},
|
|
23401
|
+
...input.cause ? { cause: input.cause } : {},
|
|
23402
|
+
...input.actorId ? { actorId: input.actorId } : {},
|
|
23403
|
+
...input.observedAt !== void 0 ? { observedAt: input.observedAt } : {},
|
|
23404
|
+
...input.force !== void 0 ? { force: input.force } : {},
|
|
23405
|
+
...input.metadata ? { metadata: input.metadata } : {}
|
|
23406
|
+
});
|
|
23407
|
+
};
|
|
23408
|
+
return new Proxy(
|
|
23409
|
+
{},
|
|
23410
|
+
{
|
|
23411
|
+
get: (_target, machineProperty) => {
|
|
23412
|
+
if (typeof machineProperty !== "string") return void 0;
|
|
23413
|
+
return new Proxy(
|
|
23414
|
+
{},
|
|
23415
|
+
{
|
|
23416
|
+
get: (_machineTarget, stateProperty) => {
|
|
23417
|
+
if (stateProperty === "to") {
|
|
23418
|
+
return (stateName, input) => observe(machineProperty, stateName, input || {});
|
|
23419
|
+
}
|
|
23420
|
+
if (typeof stateProperty !== "string") return void 0;
|
|
23421
|
+
return (input) => observe(
|
|
23422
|
+
machineProperty,
|
|
23423
|
+
stateNameFromMethodName(stateProperty),
|
|
23424
|
+
input || {}
|
|
23425
|
+
);
|
|
23426
|
+
}
|
|
23427
|
+
}
|
|
23428
|
+
);
|
|
23429
|
+
}
|
|
23430
|
+
}
|
|
23431
|
+
);
|
|
23432
|
+
}
|
|
22522
23433
|
get feedback() {
|
|
22523
23434
|
return {
|
|
22524
23435
|
list: async () => this.listFeedback()
|
|
22525
23436
|
};
|
|
22526
23437
|
}
|
|
23438
|
+
get manualActions() {
|
|
23439
|
+
return {
|
|
23440
|
+
record: (input) => this.recordManualAction(input),
|
|
23441
|
+
list: (options = {}) => this.listManualActions(options),
|
|
23442
|
+
suggest: (options = {}) => this.suggestManualActions(options)
|
|
23443
|
+
};
|
|
23444
|
+
}
|
|
23445
|
+
get artifactApprovals() {
|
|
23446
|
+
return {
|
|
23447
|
+
list: (options = {}) => this.listArtifactApprovals(options),
|
|
23448
|
+
decide: (approvalTaskId, input) => this.decideArtifactApproval(approvalTaskId, input)
|
|
23449
|
+
};
|
|
23450
|
+
}
|
|
22527
23451
|
/**
|
|
22528
23452
|
* Sessionless environments do not own a live transport, so disconnecting the
|
|
22529
23453
|
* environment handle itself is a no-op. This keeps the public surface
|
|
@@ -22533,17 +23457,12 @@ var Environment = class _Environment {
|
|
|
22533
23457
|
*/
|
|
22534
23458
|
async disconnect() {
|
|
22535
23459
|
}
|
|
22536
|
-
async listSessions(
|
|
22537
|
-
|
|
22538
|
-
|
|
22539
|
-
|
|
22540
|
-
|
|
22541
|
-
|
|
22542
|
-
return [...active, ...closed].sort(
|
|
22543
|
-
(left, right) => Date.parse(right.lastSeenAt) - Date.parse(left.lastSeenAt)
|
|
22544
|
-
);
|
|
22545
|
-
}
|
|
22546
|
-
return status === "closed" ? this.granular.listClosedSessions({ environmentId: this.environmentId }) : this.granular.listOpenSessions({ environmentId: this.environmentId });
|
|
23460
|
+
async listSessions(optionsOrStatus = {}) {
|
|
23461
|
+
const options = typeof optionsOrStatus === "string" ? { status: optionsOrStatus } : optionsOrStatus;
|
|
23462
|
+
return this.granular.listSessions({
|
|
23463
|
+
...options,
|
|
23464
|
+
environmentId: this.environmentId
|
|
23465
|
+
});
|
|
22547
23466
|
}
|
|
22548
23467
|
async getUserEnvironmentState(options = {}) {
|
|
22549
23468
|
return this.granular.getUserEnvironmentState({
|
|
@@ -22561,6 +23480,7 @@ var Environment = class _Environment {
|
|
|
22561
23480
|
return this.granular.createSession({
|
|
22562
23481
|
environmentId: this.environmentId,
|
|
22563
23482
|
clientId: options?.clientId,
|
|
23483
|
+
sessionScope: options?.sessionScope,
|
|
22564
23484
|
initialHeap: options?.initialHeap
|
|
22565
23485
|
});
|
|
22566
23486
|
}
|
|
@@ -22602,6 +23522,50 @@ var Environment = class _Environment {
|
|
|
22602
23522
|
const response = await this.controlPlaneRequest(`/control/environments/${this.environmentId}/feedback`);
|
|
22603
23523
|
return Array.isArray(response.items) ? response.items : [];
|
|
22604
23524
|
}
|
|
23525
|
+
async recordManualAction(input) {
|
|
23526
|
+
const body = {
|
|
23527
|
+
...input,
|
|
23528
|
+
...input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {}
|
|
23529
|
+
};
|
|
23530
|
+
return this.controlPlaneRequest(
|
|
23531
|
+
`/control/environments/${this.environmentId}/manual-actions`,
|
|
23532
|
+
{
|
|
23533
|
+
method: "POST",
|
|
23534
|
+
body: JSON.stringify(body)
|
|
23535
|
+
}
|
|
23536
|
+
);
|
|
23537
|
+
}
|
|
23538
|
+
async listManualActions(options = {}) {
|
|
23539
|
+
const query = new URLSearchParams();
|
|
23540
|
+
appendQueryOptions(query, options);
|
|
23541
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
23542
|
+
return this.controlPlaneRequest(`/control/environments/${this.environmentId}/manual-actions${suffix}`);
|
|
23543
|
+
}
|
|
23544
|
+
async suggestManualActions(options = {}) {
|
|
23545
|
+
const query = new URLSearchParams();
|
|
23546
|
+
appendQueryOptions(query, options);
|
|
23547
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
23548
|
+
return this.controlPlaneRequest(
|
|
23549
|
+
`/control/environments/${this.environmentId}/manual-actions/suggestions${suffix}`
|
|
23550
|
+
);
|
|
23551
|
+
}
|
|
23552
|
+
async listArtifactApprovals(options = {}) {
|
|
23553
|
+
const query = new URLSearchParams();
|
|
23554
|
+
appendQueryOptions(query, options);
|
|
23555
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
23556
|
+
return this.controlPlaneRequest(
|
|
23557
|
+
`/control/environments/${this.environmentId}/artifact-approvals${suffix}`
|
|
23558
|
+
);
|
|
23559
|
+
}
|
|
23560
|
+
async decideArtifactApproval(approvalTaskId, input) {
|
|
23561
|
+
return this.controlPlaneRequest(
|
|
23562
|
+
`/control/environments/${this.environmentId}/artifact-approvals/${encodeURIComponent(approvalTaskId)}/decide`,
|
|
23563
|
+
{
|
|
23564
|
+
method: "POST",
|
|
23565
|
+
body: JSON.stringify(input)
|
|
23566
|
+
}
|
|
23567
|
+
);
|
|
23568
|
+
}
|
|
22605
23569
|
getRuntimeBaseUrl() {
|
|
22606
23570
|
return deriveRuntimeBaseUrl(this._apiEndpoint);
|
|
22607
23571
|
}
|
|
@@ -23426,10 +24390,11 @@ var Environment = class _Environment {
|
|
|
23426
24390
|
if (!Array.isArray(records) || records.length === 0) {
|
|
23427
24391
|
return [];
|
|
23428
24392
|
}
|
|
24393
|
+
const recordsToWrite = records.map(preserveRecordObjectRealId);
|
|
23429
24394
|
const batchSize = Math.max(
|
|
23430
24395
|
1,
|
|
23431
24396
|
Math.min(
|
|
23432
|
-
|
|
24397
|
+
recordsToWrite.length,
|
|
23433
24398
|
options?.batchSize ?? DEFAULT_DIRECT_RECORD_OBJECTS_REQUEST_BATCH_SIZE
|
|
23434
24399
|
)
|
|
23435
24400
|
);
|
|
@@ -23437,8 +24402,8 @@ var Environment = class _Environment {
|
|
|
23437
24402
|
MAX_RECORD_OBJECTS_CONCURRENCY,
|
|
23438
24403
|
Math.max(1, options?.concurrency ?? 1)
|
|
23439
24404
|
);
|
|
23440
|
-
const plans = planRecordObjectsChunks(
|
|
23441
|
-
const total =
|
|
24405
|
+
const plans = planRecordObjectsChunks(recordsToWrite, batchSize);
|
|
24406
|
+
const total = recordsToWrite.length;
|
|
23442
24407
|
const results = new Array(total);
|
|
23443
24408
|
const onChunk = options?.onChunkComplete;
|
|
23444
24409
|
for (let waveStart = 0; waveStart < plans.length; waveStart += concurrency) {
|
|
@@ -23509,12 +24474,13 @@ var Environment = class _Environment {
|
|
|
23509
24474
|
* synchronous upserts and fine-grained chunk progress via **`onChunkComplete`**.
|
|
23510
24475
|
*/
|
|
23511
24476
|
async enqueueRecordImport(records, options = {}) {
|
|
24477
|
+
const recordsToImport = records.map(preserveRecordObjectRealId);
|
|
23512
24478
|
return this.controlPlaneRequest(
|
|
23513
24479
|
`/control/environments/${this.environmentId}/record-imports`,
|
|
23514
24480
|
{
|
|
23515
24481
|
method: "POST",
|
|
23516
24482
|
body: JSON.stringify({
|
|
23517
|
-
records,
|
|
24483
|
+
records: recordsToImport,
|
|
23518
24484
|
batchSize: options.batchSize,
|
|
23519
24485
|
setupRunId: options.setupRunId,
|
|
23520
24486
|
writeMode: options.writeMode
|
|
@@ -23622,11 +24588,7 @@ var EnvironmentSession = class extends Session {
|
|
|
23622
24588
|
}
|
|
23623
24589
|
buildSessionDataUrl(path7, query) {
|
|
23624
24590
|
const searchParams = new URLSearchParams();
|
|
23625
|
-
|
|
23626
|
-
if (value !== null && typeof value !== "undefined" && value !== "") {
|
|
23627
|
-
searchParams.set(key, String(value));
|
|
23628
|
-
}
|
|
23629
|
-
}
|
|
24591
|
+
appendQueryOptions(searchParams, query);
|
|
23630
24592
|
const queryString = searchParams.toString();
|
|
23631
24593
|
return `${this.environment.runtimeBaseUrl}${this.sessionDataRoutePrefix}/${encodeURIComponent(this.sessionId)}${path7}${queryString ? `?${queryString}` : ""}`;
|
|
23632
24594
|
}
|
|
@@ -23709,9 +24671,108 @@ var EnvironmentSession = class extends Session {
|
|
|
23709
24671
|
),
|
|
23710
24672
|
get: (jobId) => this.sessionDataRequest(
|
|
23711
24673
|
`/jobs/${encodeURIComponent(jobId)}`
|
|
24674
|
+
),
|
|
24675
|
+
latest: async (options = {}) => {
|
|
24676
|
+
const page = await this.sessionDataRequest("/jobs", {
|
|
24677
|
+
status: options.status || "all",
|
|
24678
|
+
latest: true,
|
|
24679
|
+
limit: 1
|
|
24680
|
+
});
|
|
24681
|
+
return page.items[0] || null;
|
|
24682
|
+
}
|
|
24683
|
+
};
|
|
24684
|
+
}
|
|
24685
|
+
get artifacts() {
|
|
24686
|
+
return {
|
|
24687
|
+
list: (options = {}) => {
|
|
24688
|
+
const queryOptions = { ...options };
|
|
24689
|
+
if (options.target) {
|
|
24690
|
+
queryOptions.targetClassName = options.target.className;
|
|
24691
|
+
queryOptions.targetId = options.target.id;
|
|
24692
|
+
delete queryOptions.target;
|
|
24693
|
+
}
|
|
24694
|
+
return this.sessionDataRequest("/artifacts", queryOptions);
|
|
24695
|
+
},
|
|
24696
|
+
listForLatestJob: (options = {}) => this.artifacts.list({
|
|
24697
|
+
...options,
|
|
24698
|
+
latestJob: true
|
|
24699
|
+
}),
|
|
24700
|
+
get: (artifactId) => this.sessionDataRequest(
|
|
24701
|
+
`/artifacts/${encodeURIComponent(artifactId)}`
|
|
24702
|
+
),
|
|
24703
|
+
create: (artifact) => this.sessionDataRequest(
|
|
24704
|
+
"/artifacts",
|
|
24705
|
+
void 0,
|
|
24706
|
+
{
|
|
24707
|
+
method: "POST",
|
|
24708
|
+
body: artifact
|
|
24709
|
+
}
|
|
24710
|
+
),
|
|
24711
|
+
updateInputs: (artifactId, patch) => this.sessionDataRequest(
|
|
24712
|
+
`/artifacts/${encodeURIComponent(artifactId)}`,
|
|
24713
|
+
void 0,
|
|
24714
|
+
{
|
|
24715
|
+
method: "PATCH",
|
|
24716
|
+
body: patch
|
|
24717
|
+
}
|
|
24718
|
+
),
|
|
24719
|
+
validate: (artifactId) => this.sessionDataRequest(
|
|
24720
|
+
`/artifacts/${encodeURIComponent(artifactId)}/validate`,
|
|
24721
|
+
void 0,
|
|
24722
|
+
{ method: "POST" }
|
|
24723
|
+
),
|
|
24724
|
+
execute: (artifactId, options) => this.sessionDataRequest(
|
|
24725
|
+
`/artifacts/${encodeURIComponent(artifactId)}/execute`,
|
|
24726
|
+
void 0,
|
|
24727
|
+
{ method: "POST", body: options }
|
|
24728
|
+
),
|
|
24729
|
+
approve: (artifactId, options) => this.sessionDataRequest(
|
|
24730
|
+
`/artifacts/${encodeURIComponent(artifactId)}/approve`,
|
|
24731
|
+
void 0,
|
|
24732
|
+
{ method: "POST", body: options }
|
|
24733
|
+
),
|
|
24734
|
+
cancel: (artifactId) => this.sessionDataRequest(
|
|
24735
|
+
`/artifacts/${encodeURIComponent(artifactId)}/cancel`,
|
|
24736
|
+
void 0,
|
|
24737
|
+
{ method: "POST" }
|
|
23712
24738
|
)
|
|
23713
24739
|
};
|
|
23714
24740
|
}
|
|
24741
|
+
get manualActions() {
|
|
24742
|
+
const useDelegatedBrowserRoute = this.sessionDataRoutePrefix === "/sdk/browser-sessions";
|
|
24743
|
+
return {
|
|
24744
|
+
record: (input) => useDelegatedBrowserRoute ? this.sessionDataRequest(
|
|
24745
|
+
"/manual-actions",
|
|
24746
|
+
void 0,
|
|
24747
|
+
{
|
|
24748
|
+
method: "POST",
|
|
24749
|
+
body: { ...input, sessionId: this.sessionId }
|
|
24750
|
+
}
|
|
24751
|
+
) : this.environment.manualActions.record({
|
|
24752
|
+
...input,
|
|
24753
|
+
sessionId: this.sessionId
|
|
24754
|
+
}),
|
|
24755
|
+
list: (options = {}) => useDelegatedBrowserRoute ? this.sessionDataRequest("/manual-actions", { ...options, sessionId: this.sessionId }) : this.environment.manualActions.list({
|
|
24756
|
+
...options,
|
|
24757
|
+
sessionId: this.sessionId
|
|
24758
|
+
}),
|
|
24759
|
+
suggest: (options = {}) => useDelegatedBrowserRoute ? this.sessionDataRequest(
|
|
24760
|
+
"/manual-actions/suggestions",
|
|
24761
|
+
options
|
|
24762
|
+
) : this.environment.manualActions.suggest(options)
|
|
24763
|
+
};
|
|
24764
|
+
}
|
|
24765
|
+
get artifactApprovals() {
|
|
24766
|
+
const useDelegatedBrowserRoute = this.sessionDataRoutePrefix === "/sdk/browser-sessions";
|
|
24767
|
+
return {
|
|
24768
|
+
list: (options = {}) => useDelegatedBrowserRoute ? this.sessionDataRequest("/artifact-approvals", options) : this.environment.artifactApprovals.list(options),
|
|
24769
|
+
decide: (approvalTaskId, input) => useDelegatedBrowserRoute ? this.sessionDataRequest(
|
|
24770
|
+
`/artifact-approvals/${encodeURIComponent(approvalTaskId)}/decide`,
|
|
24771
|
+
void 0,
|
|
24772
|
+
{ method: "POST", body: input }
|
|
24773
|
+
) : this.environment.artifactApprovals.decide(approvalTaskId, input)
|
|
24774
|
+
};
|
|
24775
|
+
}
|
|
23715
24776
|
get files() {
|
|
23716
24777
|
return {
|
|
23717
24778
|
list: (options = {}) => this.sessionDataRequest(
|
|
@@ -23792,13 +24853,16 @@ var EnvironmentSession = class extends Session {
|
|
|
23792
24853
|
get transcript() {
|
|
23793
24854
|
return {
|
|
23794
24855
|
list: async (options = {}) => {
|
|
23795
|
-
const [messages, jobs, entries, lists] = await Promise.all([
|
|
24856
|
+
const [messages, jobs, entries, lists, artifacts] = await Promise.all([
|
|
23796
24857
|
this.collectAllSessionItems(this.messages.list),
|
|
23797
24858
|
this.collectAllSessionItems(
|
|
23798
24859
|
(pageOptions) => this.jobs.list({ ...pageOptions, status: "all" })
|
|
23799
24860
|
),
|
|
23800
24861
|
this.collectAllSessionItems(this.heap.entries.list),
|
|
23801
|
-
this.collectAllSessionItems(this.heap.lists.list)
|
|
24862
|
+
this.collectAllSessionItems(this.heap.lists.list),
|
|
24863
|
+
this.collectAllSessionItems(
|
|
24864
|
+
(pageOptions) => this.artifacts.list({ ...pageOptions, status: "all" })
|
|
24865
|
+
)
|
|
23802
24866
|
]);
|
|
23803
24867
|
const liveDoc = {
|
|
23804
24868
|
conversation: { messages },
|
|
@@ -23812,6 +24876,21 @@ var EnvironmentSession = class extends Session {
|
|
|
23812
24876
|
(entry) => Boolean(entry)
|
|
23813
24877
|
)
|
|
23814
24878
|
)
|
|
24879
|
+
},
|
|
24880
|
+
artifacts: {
|
|
24881
|
+
byId: Object.fromEntries(
|
|
24882
|
+
artifacts.map((artifact) => {
|
|
24883
|
+
return artifact?.artifactId ? [
|
|
24884
|
+
artifact.artifactId,
|
|
24885
|
+
artifact
|
|
24886
|
+
] : null;
|
|
24887
|
+
}).filter(
|
|
24888
|
+
(entry) => Boolean(entry)
|
|
24889
|
+
)
|
|
24890
|
+
),
|
|
24891
|
+
order: artifacts.map((artifact) => artifact?.artifactId).filter(
|
|
24892
|
+
(artifactId) => Boolean(artifactId)
|
|
24893
|
+
)
|
|
23815
24894
|
}
|
|
23816
24895
|
};
|
|
23817
24896
|
const heap = normalizeHeapSnapshot({
|
|
@@ -23888,6 +24967,12 @@ var EnvironmentSession = class extends Session {
|
|
|
23888
24967
|
async recordObject(options) {
|
|
23889
24968
|
return this.environment.recordObject(options);
|
|
23890
24969
|
}
|
|
24970
|
+
async recordState(input) {
|
|
24971
|
+
return this.environment.recordState(input);
|
|
24972
|
+
}
|
|
24973
|
+
state(target) {
|
|
24974
|
+
return this.environment.state(target);
|
|
24975
|
+
}
|
|
23891
24976
|
async recordObjects(records, options) {
|
|
23892
24977
|
return this.environment.recordObjects(records, options);
|
|
23893
24978
|
}
|
|
@@ -23958,7 +25043,7 @@ var EnvironmentSession = class extends Session {
|
|
|
23958
25043
|
* Close only the socket transport without sending `client.goodbye`.
|
|
23959
25044
|
*/
|
|
23960
25045
|
disconnectTransport() {
|
|
23961
|
-
this.client.disconnect();
|
|
25046
|
+
this.client.disconnect({ reason: "Transport detach" });
|
|
23962
25047
|
}
|
|
23963
25048
|
/**
|
|
23964
25049
|
* Backwards-compatible alias for `disconnect()`.
|
|
@@ -24353,16 +25438,71 @@ var Granular = class _Granular {
|
|
|
24353
25438
|
};
|
|
24354
25439
|
}
|
|
24355
25440
|
/**
|
|
24356
|
-
* List
|
|
25441
|
+
* List indexed sessions using ownership filters and bounded pagination.
|
|
25442
|
+
*/
|
|
25443
|
+
async listSessions(options) {
|
|
25444
|
+
const environmentId = options.environmentId?.trim();
|
|
25445
|
+
const sandboxId = options.sandboxId?.trim();
|
|
25446
|
+
const subjectId = options.subjectId?.trim();
|
|
25447
|
+
if (!environmentId && !sandboxId && !subjectId) {
|
|
25448
|
+
throw new Error(
|
|
25449
|
+
"listSessions() requires environmentId, sandboxId, or subjectId so history cannot be scanned accidentally."
|
|
25450
|
+
);
|
|
25451
|
+
}
|
|
25452
|
+
const status = options.status || "active";
|
|
25453
|
+
const allowedStatuses = /* @__PURE__ */ new Set([
|
|
25454
|
+
"active",
|
|
25455
|
+
"closed",
|
|
25456
|
+
"expired",
|
|
25457
|
+
"failed",
|
|
25458
|
+
"timeout",
|
|
25459
|
+
"all"
|
|
25460
|
+
]);
|
|
25461
|
+
if (!allowedStatuses.has(status)) {
|
|
25462
|
+
throw new Error(`Unsupported session status: ${String(status)}`);
|
|
25463
|
+
}
|
|
25464
|
+
const limit = boundedSessionListInteger(
|
|
25465
|
+
options.limit,
|
|
25466
|
+
"limit",
|
|
25467
|
+
DEFAULT_CONVERSATION_SESSION_LIST_LIMIT,
|
|
25468
|
+
1,
|
|
25469
|
+
MAX_CONVERSATION_SESSION_LIST_LIMIT
|
|
25470
|
+
);
|
|
25471
|
+
const offset = boundedSessionListInteger(
|
|
25472
|
+
options.offset,
|
|
25473
|
+
"offset",
|
|
25474
|
+
0,
|
|
25475
|
+
0,
|
|
25476
|
+
MAX_CONVERSATION_SESSION_LIST_OFFSET
|
|
25477
|
+
);
|
|
25478
|
+
const query = new URLSearchParams({
|
|
25479
|
+
limit: String(limit),
|
|
25480
|
+
offset: String(offset)
|
|
25481
|
+
});
|
|
25482
|
+
if (environmentId) query.set("environmentId", environmentId);
|
|
25483
|
+
if (sandboxId) query.set("sandboxId", sandboxId);
|
|
25484
|
+
if (subjectId) query.set("userId", subjectId);
|
|
25485
|
+
if (options.sessionScope?.trim()) {
|
|
25486
|
+
query.set("sessionScope", options.sessionScope.trim());
|
|
25487
|
+
}
|
|
25488
|
+
if (status !== "all") query.set("status", status);
|
|
25489
|
+
const res = await this.request(
|
|
25490
|
+
`/control/sessions?${query.toString()}`
|
|
25491
|
+
);
|
|
25492
|
+
const items = Array.isArray(res.items) ? res.items : [];
|
|
25493
|
+
return items.map((row) => this.normalizeConversationSession(row));
|
|
25494
|
+
}
|
|
25495
|
+
/**
|
|
25496
|
+
* List active (open) sessions for an environment.
|
|
24357
25497
|
*/
|
|
24358
25498
|
async listOpenSessions(filters) {
|
|
24359
|
-
return this.
|
|
25499
|
+
return this.listSessions({ ...filters, status: "active" });
|
|
24360
25500
|
}
|
|
24361
25501
|
/**
|
|
24362
25502
|
* List closed sessions for an environment (conversations that have disconnected).
|
|
24363
25503
|
*/
|
|
24364
25504
|
async listClosedSessions(filters) {
|
|
24365
|
-
return this.
|
|
25505
|
+
return this.listSessions({ ...filters, status: "closed" });
|
|
24366
25506
|
}
|
|
24367
25507
|
async getUserEnvironmentState(options) {
|
|
24368
25508
|
const query = new URLSearchParams({
|
|
@@ -24397,14 +25537,6 @@ var Granular = class _Granular {
|
|
|
24397
25537
|
});
|
|
24398
25538
|
return result.readAtBySessionId || {};
|
|
24399
25539
|
}
|
|
24400
|
-
async listSessionsForEnvironment(environmentId, status) {
|
|
24401
|
-
const query = new URLSearchParams({ environmentId, status });
|
|
24402
|
-
const res = await this.request(
|
|
24403
|
-
`/control/sessions?${query.toString()}`
|
|
24404
|
-
);
|
|
24405
|
-
const items = Array.isArray(res.items) ? res.items : [];
|
|
24406
|
-
return items.map((row) => this.normalizeConversationSession(row));
|
|
24407
|
-
}
|
|
24408
25540
|
normalizeConversationSession(row) {
|
|
24409
25541
|
const sessionId = String(row.sessionId ?? row.session_id ?? "");
|
|
24410
25542
|
const environmentId = String(row.environmentId ?? row.environment_id ?? "");
|
|
@@ -24463,6 +25595,7 @@ var Granular = class _Granular {
|
|
|
24463
25595
|
*/
|
|
24464
25596
|
async createSession(options) {
|
|
24465
25597
|
const clientId = options.clientId || `client_${Date.now()}`;
|
|
25598
|
+
const sessionScope = options.sessionScope?.trim() || void 0;
|
|
24466
25599
|
await this.activateEnvironment(options.environmentId);
|
|
24467
25600
|
const envData = await this.environments.get(options.environmentId);
|
|
24468
25601
|
const environment = this.bindEnvironmentHandle(envData);
|
|
@@ -24471,6 +25604,8 @@ var Granular = class _Granular {
|
|
|
24471
25604
|
body: JSON.stringify({
|
|
24472
25605
|
environmentId: options.environmentId,
|
|
24473
25606
|
clientId,
|
|
25607
|
+
sessionScope,
|
|
25608
|
+
capabilities: sessionScope ? { sessionScope } : void 0,
|
|
24474
25609
|
initialHeap: options.initialHeap
|
|
24475
25610
|
})
|
|
24476
25611
|
});
|
|
@@ -24715,15 +25850,43 @@ var Granular = class _Granular {
|
|
|
24715
25850
|
const effects2 = Array.from(
|
|
24716
25851
|
this.getSandboxEffectMap(host.sandboxId).values()
|
|
24717
25852
|
).map((effect) => this.serializeEffect(effect));
|
|
24718
|
-
|
|
24719
|
-
|
|
24720
|
-
|
|
24721
|
-
|
|
24722
|
-
|
|
24723
|
-
|
|
24724
|
-
|
|
24725
|
-
|
|
24726
|
-
|
|
25853
|
+
let acceptedCount = 0;
|
|
25854
|
+
const rejected = [];
|
|
25855
|
+
try {
|
|
25856
|
+
await withTimeout(
|
|
25857
|
+
host.wsClient.call("effects.resetCatalog", {}),
|
|
25858
|
+
EFFECT_CATALOG_SYNC_TIMEOUT_MS,
|
|
25859
|
+
`effects.resetCatalog for sandbox ${host.sandboxId}`
|
|
25860
|
+
);
|
|
25861
|
+
for (const batch of chunkItems(effects2, EFFECT_CATALOG_SYNC_BATCH_SIZE)) {
|
|
25862
|
+
const result = await withTimeout(
|
|
25863
|
+
host.wsClient.call("effects.addCatalog", {
|
|
25864
|
+
effects: batch
|
|
25865
|
+
}),
|
|
25866
|
+
EFFECT_CATALOG_SYNC_TIMEOUT_MS,
|
|
25867
|
+
`effects.addCatalog for sandbox ${host.sandboxId}`
|
|
25868
|
+
);
|
|
25869
|
+
acceptedCount += typeof result?.acceptedCount === "number" ? result.acceptedCount : 0;
|
|
25870
|
+
if (Array.isArray(result?.rejected)) {
|
|
25871
|
+
rejected.push(...result.rejected);
|
|
25872
|
+
}
|
|
25873
|
+
}
|
|
25874
|
+
} catch (error2) {
|
|
25875
|
+
if (!isUnsupportedEffectCatalogMutation(error2)) {
|
|
25876
|
+
throw error2;
|
|
25877
|
+
}
|
|
25878
|
+
const result = await withTimeout(
|
|
25879
|
+
host.wsClient.call("effects.publishCatalog", {
|
|
25880
|
+
effects: effects2
|
|
25881
|
+
}),
|
|
25882
|
+
EFFECT_CATALOG_SYNC_TIMEOUT_MS,
|
|
25883
|
+
`effects.publishCatalog for sandbox ${host.sandboxId}`
|
|
25884
|
+
);
|
|
25885
|
+
acceptedCount = typeof result?.acceptedCount === "number" ? result.acceptedCount : 0;
|
|
25886
|
+
if (Array.isArray(result?.rejected)) {
|
|
25887
|
+
rejected.push(...result.rejected);
|
|
25888
|
+
}
|
|
25889
|
+
}
|
|
24727
25890
|
if (acceptedCount === 0 && rejected.length > 0) {
|
|
24728
25891
|
const detail = rejected.map(
|
|
24729
25892
|
(entry) => `${entry.name || "unknown"}: ${entry.reason || "rejected"}`
|
|
@@ -25764,15 +26927,22 @@ async function resolveEnvironmentData(granular, options) {
|
|
|
25764
26927
|
const ontologyId = await resolveOntologyId(granular, options.ontology);
|
|
25765
26928
|
const environmentName = options.environment ?? "dev";
|
|
25766
26929
|
const environments = await granular.environments.list(ontologyId);
|
|
25767
|
-
const
|
|
25768
|
-
(environment) => matchesEnvironmentName(environment, environmentName)
|
|
26930
|
+
const matchingEnvironments = environments.filter(
|
|
26931
|
+
(environment) => matchesEnvironmentName(environment, environmentName) && (!options.subjectId || environment.subjectId === options.subjectId)
|
|
25769
26932
|
);
|
|
26933
|
+
if (matchingEnvironments.length > 1) {
|
|
26934
|
+
throw new Error(
|
|
26935
|
+
`Environment slot \`${environmentName}\` matches ${matchingEnvironments.length} subject environments. Pass --environment-id or --subject-id so session history cannot resolve to an arbitrary user.`
|
|
26936
|
+
);
|
|
26937
|
+
}
|
|
26938
|
+
const existing = matchingEnvironments[0];
|
|
25770
26939
|
if (existing) {
|
|
25771
26940
|
return existing;
|
|
25772
26941
|
}
|
|
25773
26942
|
if (!options.createIfMissing) {
|
|
26943
|
+
const subjectSuffix = options.subjectId ? ` for subject \`${options.subjectId}\`` : "";
|
|
25774
26944
|
throw new Error(
|
|
25775
|
-
`No environment named \`${environmentName}
|
|
26945
|
+
`No environment named \`${environmentName}\`${subjectSuffix} found for ontology \`${ontologyId}\`. Run \`granular connect test\` or \`granular session create\` first, or pass \`--environment-id\`.`
|
|
25776
26946
|
);
|
|
25777
26947
|
}
|
|
25778
26948
|
const connection = await granular.connect({
|
|
@@ -25783,17 +26953,15 @@ async function resolveEnvironmentData(granular, options) {
|
|
|
25783
26953
|
});
|
|
25784
26954
|
return await granular.environments.get(connection.environmentId);
|
|
25785
26955
|
}
|
|
25786
|
-
async function listSessionsForEnvironment(granular, environmentId,
|
|
25787
|
-
|
|
25788
|
-
|
|
25789
|
-
|
|
25790
|
-
|
|
25791
|
-
|
|
25792
|
-
|
|
25793
|
-
|
|
25794
|
-
|
|
25795
|
-
}
|
|
25796
|
-
return status === "closed" ? granular.listClosedSessions({ environmentId }) : granular.listOpenSessions({ environmentId });
|
|
26956
|
+
async function listSessionsForEnvironment(granular, environmentId, options) {
|
|
26957
|
+
return granular.listSessions({
|
|
26958
|
+
environmentId,
|
|
26959
|
+
status: options.status,
|
|
26960
|
+
sessionScope: options.sessionScope,
|
|
26961
|
+
subjectId: options.subjectId,
|
|
26962
|
+
limit: options.limit,
|
|
26963
|
+
offset: options.offset
|
|
26964
|
+
});
|
|
25797
26965
|
}
|
|
25798
26966
|
async function connectRuntime(options) {
|
|
25799
26967
|
if (options.sessionId) {
|
|
@@ -25906,6 +27074,19 @@ async function connectTestCommand(options) {
|
|
|
25906
27074
|
}
|
|
25907
27075
|
|
|
25908
27076
|
// src/cli/commands/session.ts
|
|
27077
|
+
function parseSessionListInteger(value, name) {
|
|
27078
|
+
const fallback2 = name === "limit" ? 25 : 0;
|
|
27079
|
+
const minimum = name === "limit" ? 1 : 0;
|
|
27080
|
+
const maximum = name === "limit" ? 500 : 1e5;
|
|
27081
|
+
if (value === void 0 || value === "") return fallback2;
|
|
27082
|
+
const parsed = typeof value === "number" ? value : Number.parseInt(value.trim(), 10);
|
|
27083
|
+
if (!Number.isInteger(parsed) || String(parsed) !== String(value).trim() || parsed < minimum || parsed > maximum) {
|
|
27084
|
+
throw new Error(
|
|
27085
|
+
`--${name} must be an integer between ${minimum} and ${maximum}.`
|
|
27086
|
+
);
|
|
27087
|
+
}
|
|
27088
|
+
return parsed;
|
|
27089
|
+
}
|
|
25909
27090
|
function printValue(value, emitJson) {
|
|
25910
27091
|
const json = JSON.stringify(value, null, 2);
|
|
25911
27092
|
if (emitJson) {
|
|
@@ -25990,7 +27171,8 @@ async function sessionCreateCommand(options) {
|
|
|
25990
27171
|
createIfMissing: true
|
|
25991
27172
|
});
|
|
25992
27173
|
const environment = await granular.createSession({
|
|
25993
|
-
environmentId: envData.environmentId
|
|
27174
|
+
environmentId: envData.environmentId,
|
|
27175
|
+
sessionScope: options.sessionScope
|
|
25994
27176
|
});
|
|
25995
27177
|
try {
|
|
25996
27178
|
const payload = {
|
|
@@ -25999,6 +27181,7 @@ async function sessionCreateCommand(options) {
|
|
|
25999
27181
|
environmentId: environment.environmentId,
|
|
26000
27182
|
environment: environment.tag || envData.tag?.name || envData.buildPolicy.tagName || requestedEnvironment,
|
|
26001
27183
|
sessionId: environment.sessionId,
|
|
27184
|
+
sessionScope: options.sessionScope?.trim() || null,
|
|
26002
27185
|
subjectId: environment.subjectId,
|
|
26003
27186
|
versionId: environment.versionId
|
|
26004
27187
|
};
|
|
@@ -26013,6 +27196,7 @@ async function sessionCreateCommand(options) {
|
|
|
26013
27196
|
Environment: payload.environment,
|
|
26014
27197
|
"Environment ID": payload.environmentId,
|
|
26015
27198
|
"Session ID": payload.sessionId,
|
|
27199
|
+
"Session scope": payload.sessionScope || "unscoped",
|
|
26016
27200
|
"Subject ID": payload.subjectId,
|
|
26017
27201
|
"Version ID": payload.versionId
|
|
26018
27202
|
});
|
|
@@ -26028,6 +27212,21 @@ async function sessionCreateCommand(options) {
|
|
|
26028
27212
|
async function sessionListCommand(options) {
|
|
26029
27213
|
const emitJson = options.json === true;
|
|
26030
27214
|
const status = options.status ?? "active";
|
|
27215
|
+
const allowedStatuses = /* @__PURE__ */ new Set([
|
|
27216
|
+
"active",
|
|
27217
|
+
"closed",
|
|
27218
|
+
"expired",
|
|
27219
|
+
"failed",
|
|
27220
|
+
"timeout",
|
|
27221
|
+
"all"
|
|
27222
|
+
]);
|
|
27223
|
+
if (!allowedStatuses.has(status)) {
|
|
27224
|
+
throw new Error(
|
|
27225
|
+
"--status must be one of active, closed, expired, failed, timeout, or all."
|
|
27226
|
+
);
|
|
27227
|
+
}
|
|
27228
|
+
const limit = parseSessionListInteger(options.limit, "limit");
|
|
27229
|
+
const offset = parseSessionListInteger(options.offset, "offset");
|
|
26031
27230
|
const requestedEnvironment = options.environment ?? "dev";
|
|
26032
27231
|
if (!emitJson) {
|
|
26033
27232
|
printHeader();
|
|
@@ -26037,18 +27236,33 @@ async function sessionListCommand(options) {
|
|
|
26037
27236
|
ontology: options.ontology,
|
|
26038
27237
|
environment: requestedEnvironment,
|
|
26039
27238
|
environmentId: options.environmentId,
|
|
27239
|
+
subjectId: options.subjectId,
|
|
26040
27240
|
createIfMissing: false
|
|
26041
27241
|
});
|
|
26042
27242
|
const items = await listSessionsForEnvironment(
|
|
26043
27243
|
granular,
|
|
26044
27244
|
environmentData.environmentId,
|
|
26045
|
-
|
|
27245
|
+
{
|
|
27246
|
+
status,
|
|
27247
|
+
sessionScope: options.sessionScope?.trim() || void 0,
|
|
27248
|
+
subjectId: options.subjectId?.trim() || void 0,
|
|
27249
|
+
limit,
|
|
27250
|
+
offset
|
|
27251
|
+
}
|
|
26046
27252
|
);
|
|
26047
27253
|
const payload = {
|
|
26048
27254
|
ontologyId: environmentData.sandboxId,
|
|
26049
27255
|
environmentId: environmentData.environmentId,
|
|
26050
27256
|
environment: environmentData.tag?.name || environmentData.buildPolicy.tagName || requestedEnvironment,
|
|
26051
27257
|
status,
|
|
27258
|
+
sessionScope: options.sessionScope?.trim() || null,
|
|
27259
|
+
page: {
|
|
27260
|
+
limit,
|
|
27261
|
+
offset,
|
|
27262
|
+
returned: items.length,
|
|
27263
|
+
mayHaveMore: items.length === limit,
|
|
27264
|
+
nextOffset: items.length === limit ? offset + items.length : null
|
|
27265
|
+
},
|
|
26052
27266
|
items
|
|
26053
27267
|
};
|
|
26054
27268
|
if (emitJson) {
|
|
@@ -26071,6 +27285,9 @@ async function sessionListCommand(options) {
|
|
|
26071
27285
|
item.lastSeenAt
|
|
26072
27286
|
])
|
|
26073
27287
|
);
|
|
27288
|
+
info(
|
|
27289
|
+
`Showing ${items.length} session${items.length === 1 ? "" : "s"} from offset ${offset}.` + (items.length === limit ? ` Use --offset ${offset + items.length} for the next page.` : "")
|
|
27290
|
+
);
|
|
26074
27291
|
console.log();
|
|
26075
27292
|
}
|
|
26076
27293
|
|
|
@@ -26977,6 +28194,9 @@ session.command("create").description(
|
|
|
26977
28194
|
"--permissions <list>",
|
|
26978
28195
|
"Comma-separated permission profile to ensure when a named environment needs to be created",
|
|
26979
28196
|
"allow-all"
|
|
28197
|
+
).option(
|
|
28198
|
+
"--session-scope <scope>",
|
|
28199
|
+
"Application-owned history scope to persist on the new session"
|
|
26980
28200
|
).option("--json", "Print machine-readable JSON").action(
|
|
26981
28201
|
async (options) => {
|
|
26982
28202
|
try {
|
|
@@ -26987,7 +28207,7 @@ session.command("create").description(
|
|
|
26987
28207
|
}
|
|
26988
28208
|
}
|
|
26989
28209
|
);
|
|
26990
|
-
session.command("list").description("List indexed sessions for an environment").option(
|
|
28210
|
+
session.command("list").description("List one bounded page of indexed sessions for an environment").option(
|
|
26991
28211
|
"--ontology <ontologyId>",
|
|
26992
28212
|
"Override the ontology id from .granularrc"
|
|
26993
28213
|
).option(
|
|
@@ -26997,11 +28217,17 @@ session.command("list").description("List indexed sessions for an environment").
|
|
|
26997
28217
|
).option(
|
|
26998
28218
|
"--environment-id <environmentId>",
|
|
26999
28219
|
"List sessions for this exact environment id"
|
|
28220
|
+
).option(
|
|
28221
|
+
"--subject-id <subjectId>",
|
|
28222
|
+
"Resolve and constrain history to this internal Granular subject id"
|
|
28223
|
+
).option(
|
|
28224
|
+
"--session-scope <scope>",
|
|
28225
|
+
"Return only sessions owned by this application history scope"
|
|
27000
28226
|
).option(
|
|
27001
28227
|
"--status <status>",
|
|
27002
|
-
"Session status
|
|
28228
|
+
"Session status: active|closed|expired|failed|timeout|all",
|
|
27003
28229
|
"active"
|
|
27004
|
-
).option("--json", "Print machine-readable JSON").action(
|
|
28230
|
+
).option("--limit <count>", "Rows to return (1-500)", "25").option("--offset <count>", "Rows to skip (0-100000)", "0").option("--json", "Print machine-readable JSON").action(
|
|
27005
28231
|
async (options) => {
|
|
27006
28232
|
try {
|
|
27007
28233
|
await sessionListCommand(options);
|