@rowan-agent/agent 0.9.7 → 0.9.8
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 +6 -0
- package/dist/index.d.ts +621 -497
- package/dist/index.js +330 -64
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1052,6 +1052,178 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
|
|
|
1052
1052
|
};
|
|
1053
1053
|
}
|
|
1054
1054
|
|
|
1055
|
+
// src/runtime/json.ts
|
|
1056
|
+
function isPlainObject(value) {
|
|
1057
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1058
|
+
return prototype === Object.prototype || prototype === null;
|
|
1059
|
+
}
|
|
1060
|
+
function hasOnlyDataProperties(value) {
|
|
1061
|
+
return Reflect.ownKeys(value).every((key) => {
|
|
1062
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1063
|
+
return Boolean(descriptor && "value" in descriptor);
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
function visit(value, seen) {
|
|
1067
|
+
if (value === null || typeof value === "boolean" || typeof value === "string") return true;
|
|
1068
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
1069
|
+
if (typeof value !== "object" || seen.has(value)) return false;
|
|
1070
|
+
seen.add(value);
|
|
1071
|
+
try {
|
|
1072
|
+
if (Array.isArray(value)) {
|
|
1073
|
+
if (Object.getPrototypeOf(value) !== Array.prototype || !hasOnlyDataProperties(value)) return false;
|
|
1074
|
+
if (Reflect.ownKeys(value).some((key) => key !== "length" && typeof key !== "string")) return false;
|
|
1075
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
1076
|
+
if (!Object.prototype.hasOwnProperty.call(value, index) || !visit(value[index], seen)) return false;
|
|
1077
|
+
}
|
|
1078
|
+
return true;
|
|
1079
|
+
}
|
|
1080
|
+
if (!isPlainObject(value) || !hasOnlyDataProperties(value)) return false;
|
|
1081
|
+
if (Reflect.ownKeys(value).some((key) => typeof key !== "string")) return false;
|
|
1082
|
+
if (Object.prototype.hasOwnProperty.call(value, "toJSON")) return false;
|
|
1083
|
+
return Object.keys(value).every((key) => visit(value[key], seen));
|
|
1084
|
+
} finally {
|
|
1085
|
+
seen.delete(value);
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
function isJsonValue(value) {
|
|
1089
|
+
return visit(value, /* @__PURE__ */ new Set());
|
|
1090
|
+
}
|
|
1091
|
+
function assertJsonValue(value, argument = "value") {
|
|
1092
|
+
if (!isJsonValue(value)) throw new TypeError(`${argument} must be JSON-safe`);
|
|
1093
|
+
}
|
|
1094
|
+
function canonicalJson(value) {
|
|
1095
|
+
assertJsonValue(value);
|
|
1096
|
+
const encode = (candidate) => {
|
|
1097
|
+
if (candidate === null || typeof candidate !== "object") return JSON.stringify(candidate);
|
|
1098
|
+
if (Array.isArray(candidate)) return `[${candidate.map(encode).join(",")}]`;
|
|
1099
|
+
const object = candidate;
|
|
1100
|
+
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${encode(object[key])}`).join(",")}}`;
|
|
1101
|
+
};
|
|
1102
|
+
return encode(value);
|
|
1103
|
+
}
|
|
1104
|
+
function utf8ByteLength(value) {
|
|
1105
|
+
return new TextEncoder().encode(value).byteLength;
|
|
1106
|
+
}
|
|
1107
|
+
function assertUtf8ByteLimit(value, limit, argument = "value") {
|
|
1108
|
+
if (utf8ByteLength(value) > limit) throw new RangeError(`${argument} exceeds ${limit} UTF-8 bytes`);
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// src/harness/phases/interactions.ts
|
|
1112
|
+
var PhaseInteractionCancelledError = class extends Error {
|
|
1113
|
+
code = "phase_interaction_cancelled";
|
|
1114
|
+
constructor() {
|
|
1115
|
+
super("Phase interaction work was cancelled.");
|
|
1116
|
+
this.name = "PhaseInteractionCancelledError";
|
|
1117
|
+
}
|
|
1118
|
+
};
|
|
1119
|
+
var PhaseInteractionBoundary = class extends Error {
|
|
1120
|
+
constructor(state, interactions) {
|
|
1121
|
+
super("Phase interactions require host input.");
|
|
1122
|
+
this.state = state;
|
|
1123
|
+
this.interactions = interactions;
|
|
1124
|
+
this.name = "PhaseInteractionBoundary";
|
|
1125
|
+
}
|
|
1126
|
+
state;
|
|
1127
|
+
interactions;
|
|
1128
|
+
};
|
|
1129
|
+
function createPhaseInteractionDriver(state, phase, signal) {
|
|
1130
|
+
const stored = state.phaseInteractions;
|
|
1131
|
+
const requests = new Map(
|
|
1132
|
+
stored?.requests.map((request) => [request.id, { ...request }]) ?? []
|
|
1133
|
+
);
|
|
1134
|
+
const answers = new Map(Object.entries(stored?.answers ?? {}));
|
|
1135
|
+
const usedRequestIds = /* @__PURE__ */ new Set();
|
|
1136
|
+
let checkpoint = stored?.checkpoint;
|
|
1137
|
+
const snapshot = () => ({
|
|
1138
|
+
requests: [...requests.values()].map((request) => ({
|
|
1139
|
+
...request,
|
|
1140
|
+
status: answers.has(request.id) ? "answered" : request.status
|
|
1141
|
+
})),
|
|
1142
|
+
answers: Object.fromEntries(answers),
|
|
1143
|
+
...checkpoint === void 0 ? {} : { checkpoint }
|
|
1144
|
+
});
|
|
1145
|
+
const sync = () => {
|
|
1146
|
+
state.phaseInteractions = snapshot();
|
|
1147
|
+
};
|
|
1148
|
+
const assertActive = () => {
|
|
1149
|
+
if (signal?.aborted) throw new PhaseInteractionCancelledError();
|
|
1150
|
+
};
|
|
1151
|
+
const matchingStoredRequest = (input) => {
|
|
1152
|
+
const fingerprint = interactionFingerprint(input);
|
|
1153
|
+
return [...requests.values()].find((request) => !usedRequestIds.has(request.id) && interactionFingerprint(request) === fingerprint);
|
|
1154
|
+
};
|
|
1155
|
+
return {
|
|
1156
|
+
signal: signal ?? new AbortController().signal,
|
|
1157
|
+
request(input) {
|
|
1158
|
+
assertActive();
|
|
1159
|
+
if (input.id !== void 0 && input.id.trim().length === 0) {
|
|
1160
|
+
throw new TypeError("Phase interaction id must be non-empty.");
|
|
1161
|
+
}
|
|
1162
|
+
if (input.prompt.trim().length === 0) {
|
|
1163
|
+
throw new TypeError("Phase interaction prompt must be non-empty.");
|
|
1164
|
+
}
|
|
1165
|
+
if (!PHASE_INTERACTION_KINDS.includes(input.kind)) {
|
|
1166
|
+
throw new TypeError(`Unsupported Phase interaction kind: ${String(input.kind)}.`);
|
|
1167
|
+
}
|
|
1168
|
+
if (input.payload !== void 0) assertJsonValue(input.payload, "Phase interaction payload");
|
|
1169
|
+
const matched = input.id === void 0 ? matchingStoredRequest(input) : void 0;
|
|
1170
|
+
const id = input.id ?? matched?.id ?? createId("interaction");
|
|
1171
|
+
const existing = requests.get(id);
|
|
1172
|
+
if (existing) {
|
|
1173
|
+
if (interactionFingerprint(existing) !== interactionFingerprint(input)) {
|
|
1174
|
+
throw new Error(`Phase interaction ${id} was requested with different metadata.`);
|
|
1175
|
+
}
|
|
1176
|
+
usedRequestIds.add(id);
|
|
1177
|
+
return {
|
|
1178
|
+
...existing,
|
|
1179
|
+
status: answers.has(id) ? "answered" : existing.status
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
const request = {
|
|
1183
|
+
id,
|
|
1184
|
+
phase,
|
|
1185
|
+
kind: input.kind,
|
|
1186
|
+
prompt: input.prompt,
|
|
1187
|
+
...input.payload === void 0 ? {} : { payload: input.payload },
|
|
1188
|
+
createdAt: createTimestamp(),
|
|
1189
|
+
status: "pending"
|
|
1190
|
+
};
|
|
1191
|
+
requests.set(id, request);
|
|
1192
|
+
usedRequestIds.add(id);
|
|
1193
|
+
sync();
|
|
1194
|
+
return request;
|
|
1195
|
+
},
|
|
1196
|
+
pending() {
|
|
1197
|
+
return [...requests.values()].filter((request) => request.status === "pending" && !answers.has(request.id)).map((request) => ({ ...request, status: "pending" }));
|
|
1198
|
+
},
|
|
1199
|
+
answers() {
|
|
1200
|
+
return new Map(answers);
|
|
1201
|
+
},
|
|
1202
|
+
suspend(input = {}) {
|
|
1203
|
+
assertActive();
|
|
1204
|
+
const pending = this.pending();
|
|
1205
|
+
if (pending.length === 0) throw new Error("Cannot suspend without pending Phase interactions.");
|
|
1206
|
+
if (input.checkpoint !== void 0) checkpoint = input.checkpoint;
|
|
1207
|
+
state.status = "suspended";
|
|
1208
|
+
sync();
|
|
1209
|
+
throw new PhaseInteractionBoundary(state, pending);
|
|
1210
|
+
}
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
var PHASE_INTERACTION_KINDS = [
|
|
1214
|
+
"user_input",
|
|
1215
|
+
"permission",
|
|
1216
|
+
"elicitation",
|
|
1217
|
+
"confirmation"
|
|
1218
|
+
];
|
|
1219
|
+
function interactionFingerprint(value) {
|
|
1220
|
+
return canonicalJson({
|
|
1221
|
+
kind: value.kind,
|
|
1222
|
+
prompt: value.prompt,
|
|
1223
|
+
payload: value.payload ?? null
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1055
1227
|
// src/harness/phases/loader.ts
|
|
1056
1228
|
import { existsSync as existsSync2, statSync as statSync2 } from "fs";
|
|
1057
1229
|
import { readdir as readdir2 } from "fs/promises";
|
|
@@ -2539,7 +2711,9 @@ async function executeToolCalls(input) {
|
|
|
2539
2711
|
return results;
|
|
2540
2712
|
}
|
|
2541
2713
|
function createPhaseExecution(config, state, phase, messageManager, toolExecutionManager, registry) {
|
|
2714
|
+
const interaction = createPhaseInteractionDriver(state, phase.name, config.signal);
|
|
2542
2715
|
return {
|
|
2716
|
+
interaction,
|
|
2543
2717
|
snapshot() {
|
|
2544
2718
|
return {
|
|
2545
2719
|
systemPrompt: config.context.systemPrompt,
|
|
@@ -2709,62 +2883,6 @@ async function waitForBackgroundTasks(backgroundTasks) {
|
|
|
2709
2883
|
return successful;
|
|
2710
2884
|
}
|
|
2711
2885
|
|
|
2712
|
-
// src/runtime/json.ts
|
|
2713
|
-
function isPlainObject(value) {
|
|
2714
|
-
const prototype = Object.getPrototypeOf(value);
|
|
2715
|
-
return prototype === Object.prototype || prototype === null;
|
|
2716
|
-
}
|
|
2717
|
-
function hasOnlyDataProperties(value) {
|
|
2718
|
-
return Reflect.ownKeys(value).every((key) => {
|
|
2719
|
-
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
2720
|
-
return Boolean(descriptor && "value" in descriptor);
|
|
2721
|
-
});
|
|
2722
|
-
}
|
|
2723
|
-
function visit(value, seen) {
|
|
2724
|
-
if (value === null || typeof value === "boolean" || typeof value === "string") return true;
|
|
2725
|
-
if (typeof value === "number") return Number.isFinite(value);
|
|
2726
|
-
if (typeof value !== "object" || seen.has(value)) return false;
|
|
2727
|
-
seen.add(value);
|
|
2728
|
-
try {
|
|
2729
|
-
if (Array.isArray(value)) {
|
|
2730
|
-
if (Object.getPrototypeOf(value) !== Array.prototype || !hasOnlyDataProperties(value)) return false;
|
|
2731
|
-
if (Reflect.ownKeys(value).some((key) => key !== "length" && typeof key !== "string")) return false;
|
|
2732
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
2733
|
-
if (!Object.prototype.hasOwnProperty.call(value, index) || !visit(value[index], seen)) return false;
|
|
2734
|
-
}
|
|
2735
|
-
return true;
|
|
2736
|
-
}
|
|
2737
|
-
if (!isPlainObject(value) || !hasOnlyDataProperties(value)) return false;
|
|
2738
|
-
if (Reflect.ownKeys(value).some((key) => typeof key !== "string")) return false;
|
|
2739
|
-
if (Object.prototype.hasOwnProperty.call(value, "toJSON")) return false;
|
|
2740
|
-
return Object.keys(value).every((key) => visit(value[key], seen));
|
|
2741
|
-
} finally {
|
|
2742
|
-
seen.delete(value);
|
|
2743
|
-
}
|
|
2744
|
-
}
|
|
2745
|
-
function isJsonValue(value) {
|
|
2746
|
-
return visit(value, /* @__PURE__ */ new Set());
|
|
2747
|
-
}
|
|
2748
|
-
function assertJsonValue(value, argument = "value") {
|
|
2749
|
-
if (!isJsonValue(value)) throw new TypeError(`${argument} must be JSON-safe`);
|
|
2750
|
-
}
|
|
2751
|
-
function canonicalJson(value) {
|
|
2752
|
-
assertJsonValue(value);
|
|
2753
|
-
const encode = (candidate) => {
|
|
2754
|
-
if (candidate === null || typeof candidate !== "object") return JSON.stringify(candidate);
|
|
2755
|
-
if (Array.isArray(candidate)) return `[${candidate.map(encode).join(",")}]`;
|
|
2756
|
-
const object = candidate;
|
|
2757
|
-
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${encode(object[key])}`).join(",")}}`;
|
|
2758
|
-
};
|
|
2759
|
-
return encode(value);
|
|
2760
|
-
}
|
|
2761
|
-
function utf8ByteLength(value) {
|
|
2762
|
-
return new TextEncoder().encode(value).byteLength;
|
|
2763
|
-
}
|
|
2764
|
-
function assertUtf8ByteLimit(value, limit, argument = "value") {
|
|
2765
|
-
if (utf8ByteLength(value) > limit) throw new RangeError(`${argument} exceeds ${limit} UTF-8 bytes`);
|
|
2766
|
-
}
|
|
2767
|
-
|
|
2768
2886
|
// src/runtime/execution.ts
|
|
2769
2887
|
var EXECUTION_CHECKPOINT_CODEC = "rowan.agent.execution";
|
|
2770
2888
|
var EXECUTION_CHECKPOINT_VERSION = 1;
|
|
@@ -2813,7 +2931,8 @@ function encodeExecutionCheckpoint(state) {
|
|
|
2813
2931
|
currentPhase: state.currentPhase,
|
|
2814
2932
|
attempt: state.attempt,
|
|
2815
2933
|
metrics,
|
|
2816
|
-
...continuation ? { continuation } : {}
|
|
2934
|
+
...continuation ? { continuation } : {},
|
|
2935
|
+
...state.phaseInteractions === void 0 ? {} : { phaseInteractions: state.phaseInteractions }
|
|
2817
2936
|
};
|
|
2818
2937
|
assertJsonValue(data, "execution checkpoint");
|
|
2819
2938
|
return {
|
|
@@ -2844,6 +2963,13 @@ function decodeExecutionCheckpoint(checkpoint) {
|
|
|
2844
2963
|
...checkpoint.data.continuation,
|
|
2845
2964
|
previousResults: checkpoint.data.continuation.previousResults.map((result) => ({ ...result }))
|
|
2846
2965
|
}
|
|
2966
|
+
} : {},
|
|
2967
|
+
...checkpoint.data.phaseInteractions ? {
|
|
2968
|
+
phaseInteractions: {
|
|
2969
|
+
requests: checkpoint.data.phaseInteractions.requests.map((request) => ({ ...request })),
|
|
2970
|
+
answers: { ...checkpoint.data.phaseInteractions.answers },
|
|
2971
|
+
...checkpoint.data.phaseInteractions.checkpoint === void 0 ? {} : { checkpoint: checkpoint.data.phaseInteractions.checkpoint }
|
|
2972
|
+
}
|
|
2847
2973
|
} : {}
|
|
2848
2974
|
};
|
|
2849
2975
|
}
|
|
@@ -2869,6 +2995,16 @@ async function executeOnce(input) {
|
|
|
2869
2995
|
status: "running",
|
|
2870
2996
|
metrics: createMetrics()
|
|
2871
2997
|
};
|
|
2998
|
+
if (input.interactionAnswers) {
|
|
2999
|
+
state.phaseInteractions = {
|
|
3000
|
+
requests: state.phaseInteractions?.requests ?? [],
|
|
3001
|
+
answers: {
|
|
3002
|
+
...state.phaseInteractions?.answers ?? {},
|
|
3003
|
+
...input.interactionAnswers
|
|
3004
|
+
},
|
|
3005
|
+
...state.phaseInteractions?.checkpoint === void 0 ? {} : { checkpoint: state.phaseInteractions.checkpoint }
|
|
3006
|
+
};
|
|
3007
|
+
}
|
|
2872
3008
|
const config = {
|
|
2873
3009
|
context,
|
|
2874
3010
|
execution: input.execution,
|
|
@@ -2904,6 +3040,20 @@ async function executeOnce(input) {
|
|
|
2904
3040
|
return {
|
|
2905
3041
|
type: "input_required",
|
|
2906
3042
|
request: error.request,
|
|
3043
|
+
interactions: [],
|
|
3044
|
+
checkpoint: encodeExecutionCheckpoint(error.state),
|
|
3045
|
+
messages: snapshotMessages(context.messages)
|
|
3046
|
+
};
|
|
3047
|
+
}
|
|
3048
|
+
if (error instanceof PhaseInteractionBoundary) {
|
|
3049
|
+
return {
|
|
3050
|
+
type: "input_required",
|
|
3051
|
+
request: {
|
|
3052
|
+
phase: error.interactions[0]?.phase ?? state.currentPhase,
|
|
3053
|
+
prompt: error.interactions.map((interaction) => interaction.prompt).join("\n"),
|
|
3054
|
+
requestedAt: error.interactions[0]?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
3055
|
+
},
|
|
3056
|
+
interactions: error.interactions,
|
|
2907
3057
|
checkpoint: encodeExecutionCheckpoint(error.state),
|
|
2908
3058
|
messages: snapshotMessages(context.messages)
|
|
2909
3059
|
};
|
|
@@ -2933,8 +3083,15 @@ function isCheckpointData(value) {
|
|
|
2933
3083
|
if (!isRecord2(value) || typeof value.currentPhase !== "string" || !Number.isInteger(value.attempt)) return false;
|
|
2934
3084
|
if (!isMetrics(value.metrics)) return false;
|
|
2935
3085
|
if (value.continuation !== void 0 && !isContinuation(value.continuation)) return false;
|
|
3086
|
+
if (value.phaseInteractions !== void 0 && !isPhaseInteractionState(value.phaseInteractions)) return false;
|
|
2936
3087
|
return true;
|
|
2937
3088
|
}
|
|
3089
|
+
function isPhaseInteractionState(value) {
|
|
3090
|
+
if (!isRecord2(value) || !Array.isArray(value.requests) || !isRecord2(value.answers)) return false;
|
|
3091
|
+
if (!Object.values(value.answers).every((answer) => isJsonValue(answer))) return false;
|
|
3092
|
+
if (value.checkpoint !== void 0 && !isJsonValue(value.checkpoint)) return false;
|
|
3093
|
+
return value.requests.every((request) => isRecord2(request) && typeof request.id === "string" && typeof request.phase === "string" && ["user_input", "permission", "elicitation", "confirmation"].includes(request.kind) && typeof request.prompt === "string" && typeof request.createdAt === "string" && ["pending", "answered", "denied", "cancelled", "expired"].includes(request.status) && (request.payload === void 0 || isJsonValue(request.payload)));
|
|
3094
|
+
}
|
|
2938
3095
|
function isMetrics(value) {
|
|
2939
3096
|
if (!isRecord2(value)) return false;
|
|
2940
3097
|
return Number.isInteger(value.iterations) && Array.isArray(value.phaseTransitions) && value.phaseTransitions.every((transition) => isRecord2(transition) && typeof transition.from === "string" && typeof transition.to === "string" && typeof transition.ts === "string") && Number.isInteger(value.compactionCount) && Number.isInteger(value.retryCount) && typeof value.startedAt === "string" && Number.isFinite(value.startedAtMs) && (value.endedAt === void 0 || typeof value.endedAt === "string") && (value.durationMs === void 0 || Number.isFinite(value.durationMs));
|
|
@@ -5412,6 +5569,15 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
5412
5569
|
await this.owned.answerInput({ runId, requestId: input.requestId, expectedRevision: snapshot.revision, input: input.input });
|
|
5413
5570
|
void this.pump();
|
|
5414
5571
|
}
|
|
5572
|
+
async respondInteraction(runId, input) {
|
|
5573
|
+
this.assertOpen();
|
|
5574
|
+
const snapshot = await this.owned.snapshotRun(runId);
|
|
5575
|
+
if (snapshot.state !== "input_required" || !snapshot.interactions.some((interaction) => interaction.id === input.interactionId)) {
|
|
5576
|
+
throw new RuntimeError("input_request_conflict", { runId, requestId: input.interactionId, reason: "not_found" });
|
|
5577
|
+
}
|
|
5578
|
+
await this.owned.answerInteraction({ runId, interactionId: input.interactionId, expectedRevision: snapshot.revision, input: input.input });
|
|
5579
|
+
void this.pump();
|
|
5580
|
+
}
|
|
5415
5581
|
async cancel(runId, reason) {
|
|
5416
5582
|
this.assertOpen();
|
|
5417
5583
|
const done = this.executionDone.get(runId);
|
|
@@ -5541,6 +5707,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
5541
5707
|
stream,
|
|
5542
5708
|
maxAttempts: config.maxAttempts,
|
|
5543
5709
|
checkpoint: claim.run.checkpoint,
|
|
5710
|
+
interactionAnswers: claim.run.interactionAnswers,
|
|
5544
5711
|
signal: controller.signal,
|
|
5545
5712
|
beforePhase: assembly.beforePhase,
|
|
5546
5713
|
afterPhase: assembly.afterPhase,
|
|
@@ -5610,7 +5777,17 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
5610
5777
|
}
|
|
5611
5778
|
if (result.type === "input_required") {
|
|
5612
5779
|
const prompt = promptMessage(run, result.request.prompt, result.messages.length);
|
|
5613
|
-
await this.owned.commitInputRequired({
|
|
5780
|
+
await this.owned.commitInputRequired({
|
|
5781
|
+
runId: run.id,
|
|
5782
|
+
execution: claim.execution,
|
|
5783
|
+
expectedRevision: executionRevision,
|
|
5784
|
+
requestId: createId("input"),
|
|
5785
|
+
phase: result.request.phase,
|
|
5786
|
+
prompt,
|
|
5787
|
+
checkpoint: result.checkpoint,
|
|
5788
|
+
interactions: result.interactions,
|
|
5789
|
+
interactionAnswers: claim.run.interactionAnswers
|
|
5790
|
+
});
|
|
5614
5791
|
this.transientEvents.clear(run.id, claim.execution.executionId);
|
|
5615
5792
|
return;
|
|
5616
5793
|
}
|
|
@@ -5873,6 +6050,9 @@ var DurableRun = class {
|
|
|
5873
6050
|
respond(input) {
|
|
5874
6051
|
return this.runtime.respond(this.id, input);
|
|
5875
6052
|
}
|
|
6053
|
+
respondInteraction(input) {
|
|
6054
|
+
return this.runtime.respondInteraction(this.id, input);
|
|
6055
|
+
}
|
|
5876
6056
|
cancel(reason) {
|
|
5877
6057
|
return this.runtime.cancel(this.id, reason);
|
|
5878
6058
|
}
|
|
@@ -5899,7 +6079,14 @@ function durableOutcome(outcome) {
|
|
|
5899
6079
|
function boundaryFromSnapshot(snapshot) {
|
|
5900
6080
|
switch (snapshot.state) {
|
|
5901
6081
|
case "input_required":
|
|
5902
|
-
return {
|
|
6082
|
+
return {
|
|
6083
|
+
type: "input_required",
|
|
6084
|
+
requestId: snapshot.request.id,
|
|
6085
|
+
phase: snapshot.request.phase,
|
|
6086
|
+
prompt: snapshot.request.prompt,
|
|
6087
|
+
interactions: snapshot.interactions,
|
|
6088
|
+
answers: snapshot.answers
|
|
6089
|
+
};
|
|
5903
6090
|
case "completed":
|
|
5904
6091
|
return { type: "completed", outcome: snapshot.outcome, ...snapshot.output ? { output: snapshot.output } : {} };
|
|
5905
6092
|
case "failed":
|
|
@@ -6395,7 +6582,8 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
6395
6582
|
if (typeof input.phase !== "string" || input.phase.length === 0) throw new TypeError("phase must be non-empty");
|
|
6396
6583
|
const requestId = input.requestId ?? createId("input");
|
|
6397
6584
|
const operationKey = `input_required:${requestId}`;
|
|
6398
|
-
const
|
|
6585
|
+
const interactions = input.interactions ?? [];
|
|
6586
|
+
const operationPayload = canonicalJson([input.runId, input.expectedRevision, input.phase, input.prompt.id, input.checkpoint, interactions, input.interactionAnswers ?? null]);
|
|
6399
6587
|
const replay = this.replayOperation(operationKey, operationPayload);
|
|
6400
6588
|
if (replay) return clone(replay);
|
|
6401
6589
|
const run = this.requireRun(input.runId);
|
|
@@ -6416,12 +6604,19 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
6416
6604
|
run.state = "input_required";
|
|
6417
6605
|
run.checkpoint = clone(input.checkpoint);
|
|
6418
6606
|
run.openInputRequest = request;
|
|
6607
|
+
if (interactions.length > 0) {
|
|
6608
|
+
run.openInteractions = clone(interactions);
|
|
6609
|
+
run.interactionAnswers = clone(input.interactionAnswers ?? run.interactionAnswers ?? {});
|
|
6610
|
+
} else {
|
|
6611
|
+
delete run.openInteractions;
|
|
6612
|
+
delete run.interactionAnswers;
|
|
6613
|
+
}
|
|
6419
6614
|
delete run.execution;
|
|
6420
6615
|
run.revision += 1;
|
|
6421
6616
|
run.updatedAt = createTimestamp();
|
|
6422
6617
|
this.appendMessage(run, prompt);
|
|
6423
|
-
this.appendTransition(run, "running", "input_required", request, prompt);
|
|
6424
|
-
const result = { run: clone(run), prompt: clone(prompt), request: clone(request) };
|
|
6618
|
+
this.appendTransition(run, "running", "input_required", request, prompt, void 0, void 0, void 0, interactions, run.interactionAnswers ?? {});
|
|
6619
|
+
const result = { run: clone(run), prompt: clone(prompt), request: clone(request), interactions: clone(interactions) };
|
|
6425
6620
|
this.writeOperationReceipt(operationKey, operationPayload, result);
|
|
6426
6621
|
return result;
|
|
6427
6622
|
}
|
|
@@ -6438,6 +6633,9 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
6438
6633
|
if (!run.openInputRequest || run.openInputRequest.id !== input.requestId) {
|
|
6439
6634
|
throw new RuntimeError("input_request_conflict", { runId: run.id, requestId: input.requestId, reason: "not_found" });
|
|
6440
6635
|
}
|
|
6636
|
+
if (run.openInteractions && run.openInteractions.length > 0) {
|
|
6637
|
+
throw new RuntimeError("input_request_conflict", { runId: run.id, requestId: input.requestId, reason: "not_found" });
|
|
6638
|
+
}
|
|
6441
6639
|
const message = {
|
|
6442
6640
|
id: input.messageId ?? createId("msg"),
|
|
6443
6641
|
agentId: run.agentId,
|
|
@@ -6460,6 +6658,50 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
6460
6658
|
this.writeOperationReceipt(operationKey, operationPayload, result);
|
|
6461
6659
|
return result;
|
|
6462
6660
|
}
|
|
6661
|
+
answerInteraction(lease, input) {
|
|
6662
|
+
this.assertOwner(lease);
|
|
6663
|
+
if (input.interactionId.trim().length === 0) throw new TypeError("interactionId must be non-empty");
|
|
6664
|
+
const operationKey = `interaction_answer:${input.runId}:${input.interactionId}`;
|
|
6665
|
+
const operationPayload = canonicalJson(input.input);
|
|
6666
|
+
const replay = this.replayOperation(operationKey, operationPayload);
|
|
6667
|
+
if (replay) return clone(replay);
|
|
6668
|
+
const run = this.requireRun(input.runId);
|
|
6669
|
+
this.assertRevision(run, input.expectedRevision);
|
|
6670
|
+
this.assertState(run, ["input_required"]);
|
|
6671
|
+
const openInteractions = run.openInteractions ?? [];
|
|
6672
|
+
if (!openInteractions.some((interaction) => interaction.id === input.interactionId)) {
|
|
6673
|
+
throw new RuntimeError("input_request_conflict", { runId: run.id, requestId: input.interactionId, reason: "not_found" });
|
|
6674
|
+
}
|
|
6675
|
+
const message = {
|
|
6676
|
+
id: createId("msg"),
|
|
6677
|
+
agentId: run.agentId,
|
|
6678
|
+
runId: run.id,
|
|
6679
|
+
role: "user",
|
|
6680
|
+
content: typeof input.input === "string" ? input.input : JSON.stringify(input.input),
|
|
6681
|
+
metadata: { kind: "phase_interaction", interactionId: input.interactionId },
|
|
6682
|
+
sequenceWithinRun: this.nextMessageSequence(run.id),
|
|
6683
|
+
createdAt: createTimestamp()
|
|
6684
|
+
};
|
|
6685
|
+
this.messages.set(message.id, message);
|
|
6686
|
+
run.openInteractions = openInteractions.filter((interaction) => interaction.id !== input.interactionId);
|
|
6687
|
+
run.interactionAnswers = {
|
|
6688
|
+
...run.interactionAnswers ?? {},
|
|
6689
|
+
[input.interactionId]: clone(input.input)
|
|
6690
|
+
};
|
|
6691
|
+
run.revision += 1;
|
|
6692
|
+
run.updatedAt = message.createdAt;
|
|
6693
|
+
this.appendMessage(run, message);
|
|
6694
|
+
if (run.openInteractions.length === 0) {
|
|
6695
|
+
delete run.openInteractions;
|
|
6696
|
+
delete run.openInputRequest;
|
|
6697
|
+
run.readySequence = this.nextReady(run.agentId);
|
|
6698
|
+
run.state = "queued";
|
|
6699
|
+
this.appendTransition(run, "input_required", "queued");
|
|
6700
|
+
}
|
|
6701
|
+
const result = clone(run);
|
|
6702
|
+
this.writeOperationReceipt(operationKey, operationPayload, result);
|
|
6703
|
+
return result;
|
|
6704
|
+
}
|
|
6463
6705
|
reserveToolCall(lease, input) {
|
|
6464
6706
|
const providerToolCallId = input.providerToolCallId ?? (input.toolCallId ? String(input.toolCallId) : createId("provider"));
|
|
6465
6707
|
const result = this.reserveToolCalls(lease, {
|
|
@@ -6654,6 +6896,8 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
6654
6896
|
if (input.outcome) run.outcome = clone(input.outcome);
|
|
6655
6897
|
if (input.failure) run.failure = clone(input.failure);
|
|
6656
6898
|
delete run.execution;
|
|
6899
|
+
delete run.openInteractions;
|
|
6900
|
+
delete run.interactionAnswers;
|
|
6657
6901
|
run.revision += 1;
|
|
6658
6902
|
run.updatedAt = createTimestamp();
|
|
6659
6903
|
this.appendTransition(run, "running", nextState, void 0, output, input.outcome, input.failure);
|
|
@@ -6699,6 +6943,8 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
6699
6943
|
delete run.cancellationReason;
|
|
6700
6944
|
delete run.execution;
|
|
6701
6945
|
delete run.openInputRequest;
|
|
6946
|
+
delete run.openInteractions;
|
|
6947
|
+
delete run.interactionAnswers;
|
|
6702
6948
|
run.revision += 1;
|
|
6703
6949
|
run.updatedAt = createTimestamp();
|
|
6704
6950
|
this.appendTransition(run, from, "failed", void 0, void 0, void 0, failure);
|
|
@@ -6710,6 +6956,8 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
6710
6956
|
run.cancellationReason = input.reason;
|
|
6711
6957
|
delete run.execution;
|
|
6712
6958
|
delete run.openInputRequest;
|
|
6959
|
+
delete run.openInteractions;
|
|
6960
|
+
delete run.interactionAnswers;
|
|
6713
6961
|
run.revision += 1;
|
|
6714
6962
|
run.updatedAt = createTimestamp();
|
|
6715
6963
|
this.appendTransition(run, from, "cancelled", void 0, void 0, void 0, void 0, input.reason);
|
|
@@ -6741,7 +6989,13 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
6741
6989
|
const request = run.openInputRequest;
|
|
6742
6990
|
const prompt = request && this.messages.get(request.messageId);
|
|
6743
6991
|
if (!prompt || prompt.role !== "assistant") throw new Error(`Run ${run.id} has an invalid input prompt.`);
|
|
6744
|
-
return {
|
|
6992
|
+
return {
|
|
6993
|
+
...base,
|
|
6994
|
+
state: "input_required",
|
|
6995
|
+
request: { id: request.id, phase: request.phase, prompt: clone(prompt) },
|
|
6996
|
+
interactions: clone(run.openInteractions ?? []),
|
|
6997
|
+
answers: clone(run.interactionAnswers ?? {})
|
|
6998
|
+
};
|
|
6745
6999
|
}
|
|
6746
7000
|
case "completed": {
|
|
6747
7001
|
const output = this.messagesForRun(run.id).filter((message) => message.role === "assistant").at(-1);
|
|
@@ -6963,13 +7217,14 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
6963
7217
|
message
|
|
6964
7218
|
}));
|
|
6965
7219
|
}
|
|
6966
|
-
appendTransition(run, from, to, request, prompt, outcome, failure, reason) {
|
|
7220
|
+
appendTransition(run, from, to, request, prompt, outcome, failure, reason, interactions, answers) {
|
|
6967
7221
|
const transition = {
|
|
6968
7222
|
...this.baseEvent(run),
|
|
6969
7223
|
kind: "run_state_changed",
|
|
6970
7224
|
from,
|
|
6971
7225
|
to,
|
|
6972
7226
|
...to === "input_required" && request && prompt ? { request: { id: request.id, phase: request.phase, prompt } } : {},
|
|
7227
|
+
...to === "input_required" ? { interactions: clone(interactions ?? []), answers: clone(answers ?? {}) } : {},
|
|
6973
7228
|
...to === "completed" && outcome ? { outcome } : {},
|
|
6974
7229
|
...to === "failed" && failure ? { failure } : {},
|
|
6975
7230
|
...to === "cancelled" && reason ? { reason } : {}
|
|
@@ -7047,6 +7302,9 @@ var MemoryOwnedStore = class {
|
|
|
7047
7302
|
async answerInput(input) {
|
|
7048
7303
|
return this.store.answerInput(this.lease, input);
|
|
7049
7304
|
}
|
|
7305
|
+
async answerInteraction(input) {
|
|
7306
|
+
return this.store.answerInteraction(this.lease, input);
|
|
7307
|
+
}
|
|
7050
7308
|
async commitOutcome(input) {
|
|
7051
7309
|
return this.store.commitOutcome(this.lease, input);
|
|
7052
7310
|
}
|
|
@@ -7348,6 +7606,9 @@ var SqliteStore = class {
|
|
|
7348
7606
|
async answerInput(lease, input) {
|
|
7349
7607
|
return this.invoke(lease, (store, current) => store.answerInput(current, input));
|
|
7350
7608
|
}
|
|
7609
|
+
async answerInteraction(lease, input) {
|
|
7610
|
+
return this.invoke(lease, (store, current) => store.answerInteraction(current, input));
|
|
7611
|
+
}
|
|
7351
7612
|
async commitOutcome(lease, input) {
|
|
7352
7613
|
return this.invoke(lease, (store, current) => store.commitOutcome(current, input));
|
|
7353
7614
|
}
|
|
@@ -7665,6 +7926,9 @@ var SqliteOwnedStore = class {
|
|
|
7665
7926
|
answerInput(input) {
|
|
7666
7927
|
return this.store.answerInput(this.lease, input);
|
|
7667
7928
|
}
|
|
7929
|
+
answerInteraction(input) {
|
|
7930
|
+
return this.store.answerInteraction(this.lease, input);
|
|
7931
|
+
}
|
|
7668
7932
|
commitOutcome(input) {
|
|
7669
7933
|
return this.store.commitOutcome(this.lease, input);
|
|
7670
7934
|
}
|
|
@@ -7767,6 +8031,8 @@ export {
|
|
|
7767
8031
|
ExtensionLifetimeError,
|
|
7768
8032
|
InMemoryConfigProvider,
|
|
7769
8033
|
InMemoryStore,
|
|
8034
|
+
PhaseInteractionBoundary,
|
|
8035
|
+
PhaseInteractionCancelledError,
|
|
7770
8036
|
ResourceRegistry,
|
|
7771
8037
|
ResourceRegistryError,
|
|
7772
8038
|
RuntimeBootstrapRegistry,
|