@raisindb/client 0.5.8 → 0.5.10
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 +32 -0
- package/dist/{chunk-IFV4ECHC.mjs → chunk-ASOWGNQZ.mjs} +3 -3
- package/dist/{chunk-ZUUSBLVP.mjs → chunk-FYWYSNQG.mjs} +3 -3
- package/dist/{chunk-765DLJMZ.mjs → chunk-NOPZAMIV.mjs} +31 -2
- package/dist/chunk-NOPZAMIV.mjs.map +1 -0
- package/dist/{chunk-AULHRX5C.mjs → chunk-TPZNJNZR.mjs} +2 -2
- package/dist/{chunk-VMUT6PIO.mjs → chunk-ZCRYQCXS.mjs} +2 -2
- package/dist/{conversation-list-store-BaeGM2lx.d.mts → conversation-list-store-CqgIk8Rn.d.mts} +915 -3
- package/dist/{conversation-list-store-BaeGM2lx.d.ts → conversation-list-store-CqgIk8Rn.d.ts} +915 -3
- package/dist/{index-C4pX6FVI.d.mts → index-BD3rutlF.d.mts} +1 -1
- package/dist/{index-DCtNAWZ0.d.ts → index-CDUAMIub.d.ts} +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +934 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +902 -10
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +30 -1
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +3 -3
- package/dist/{svelte-sql-DN3NpM4c.d.ts → svelte-sql-D9YU1wYS.d.ts} +1 -1
- package/dist/{svelte-sql-BhMxrqn_.d.mts → svelte-sql-l1wmTWM4.d.mts} +1 -1
- package/dist/svelte.d.mts +2 -2
- package/dist/svelte.d.ts +2 -2
- package/dist/svelte.js +30 -1
- package/dist/svelte.js.map +1 -1
- package/dist/svelte.mjs +3 -3
- package/dist/vue.d.mts +2 -2
- package/dist/vue.d.ts +2 -2
- package/dist/vue.js +30 -1
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +2 -2
- package/package.json +1 -1
- package/dist/chunk-765DLJMZ.mjs.map +0 -1
- /package/dist/{chunk-IFV4ECHC.mjs.map → chunk-ASOWGNQZ.mjs.map} +0 -0
- /package/dist/{chunk-ZUUSBLVP.mjs.map → chunk-FYWYSNQG.mjs.map} +0 -0
- /package/dist/{chunk-AULHRX5C.mjs.map → chunk-TPZNJNZR.mjs.map} +0 -0
- /package/dist/{chunk-VMUT6PIO.mjs.map → chunk-ZCRYQCXS.mjs.map} +0 -0
package/dist/index.mjs
CHANGED
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
useConversation,
|
|
4
4
|
useConversationList,
|
|
5
5
|
useFlow
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ASOWGNQZ.mjs";
|
|
7
7
|
import {
|
|
8
8
|
RAISIN_CONTEXT_KEY,
|
|
9
9
|
createConversationAdapter,
|
|
10
10
|
createConversationListAdapter,
|
|
11
11
|
createFlowAdapter
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-FYWYSNQG.mjs";
|
|
13
13
|
import {
|
|
14
14
|
FlowClient,
|
|
15
15
|
RaisinAbortError,
|
|
@@ -20,10 +20,10 @@ import {
|
|
|
20
20
|
RaisinTimeoutError,
|
|
21
21
|
SSEClient,
|
|
22
22
|
classifyHttpError
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-TPZNJNZR.mjs";
|
|
24
24
|
import {
|
|
25
25
|
createRaisinVue
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-ZCRYQCXS.mjs";
|
|
27
27
|
import {
|
|
28
28
|
createAuthAdapter,
|
|
29
29
|
createConnectionAdapter,
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
normalizeHomePath,
|
|
49
49
|
projectPlansFromMessages,
|
|
50
50
|
setLogLevel
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-NOPZAMIV.mjs";
|
|
52
52
|
|
|
53
53
|
// src/client.ts
|
|
54
54
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
@@ -491,12 +491,23 @@ var RequestType = /* @__PURE__ */ ((RequestType2) => {
|
|
|
491
491
|
RequestType2["FlowCancel"] = "flow_cancel";
|
|
492
492
|
RequestType2["FlowSubscribeEvents"] = "flow_subscribe_events";
|
|
493
493
|
RequestType2["FlowUnsubscribeEvents"] = "flow_unsubscribe_events";
|
|
494
|
+
RequestType2["AgentRunCreate"] = "agent_run_create";
|
|
495
|
+
RequestType2["AgentRunGet"] = "agent_run_get";
|
|
496
|
+
RequestType2["AgentRunBySubject"] = "agent_run_by_subject";
|
|
497
|
+
RequestType2["AgentRunList"] = "agent_run_list";
|
|
498
|
+
RequestType2["AgentRunEvents"] = "agent_run_events";
|
|
499
|
+
RequestType2["AgentRunSubscribe"] = "agent_run_subscribe";
|
|
500
|
+
RequestType2["AgentRunUnsubscribe"] = "agent_run_unsubscribe";
|
|
501
|
+
RequestType2["AgentRunControl"] = "agent_run_control";
|
|
502
|
+
RequestType2["AgentRunChildren"] = "agent_run_children";
|
|
494
503
|
RequestType2["FunctionInvoke"] = "function_invoke";
|
|
495
504
|
RequestType2["FunctionInvokeSync"] = "function_invoke_sync";
|
|
496
505
|
RequestType2["ScheduledInvocationCreate"] = "scheduled_invocation_create";
|
|
497
506
|
RequestType2["ScheduledInvocationCancel"] = "scheduled_invocation_cancel";
|
|
498
507
|
RequestType2["ScheduledInvocationList"] = "scheduled_invocation_list";
|
|
499
508
|
RequestType2["ScheduledInvocationGet"] = "scheduled_invocation_get";
|
|
509
|
+
RequestType2["VisitorChatStart"] = "visitor_chat_start";
|
|
510
|
+
RequestType2["VisitorChatSend"] = "visitor_chat_send";
|
|
500
511
|
return RequestType2;
|
|
501
512
|
})(RequestType || {});
|
|
502
513
|
var ResponseStatus = /* @__PURE__ */ ((ResponseStatus2) => {
|
|
@@ -780,7 +791,8 @@ var EventHandler = class {
|
|
|
780
791
|
this.filterSubscriptions.set(hash, {
|
|
781
792
|
serverId: subscriptionId,
|
|
782
793
|
filters: {},
|
|
783
|
-
callbacks: /* @__PURE__ */ new Set([callback])
|
|
794
|
+
callbacks: /* @__PURE__ */ new Set([callback]),
|
|
795
|
+
synthetic: true
|
|
784
796
|
});
|
|
785
797
|
this.subscriptions.set(subscriptionId, hash);
|
|
786
798
|
this.callbackToHash.set(callback, hash);
|
|
@@ -815,11 +827,12 @@ var EventHandler = class {
|
|
|
815
827
|
async restoreSubscriptions(options) {
|
|
816
828
|
if (!options?.retry) {
|
|
817
829
|
for (const entry of this.filterSubscriptions.values()) {
|
|
830
|
+
if (entry.synthetic) continue;
|
|
818
831
|
this.subscriptions.delete(entry.serverId);
|
|
819
832
|
entry.restorePending = true;
|
|
820
833
|
}
|
|
821
834
|
}
|
|
822
|
-
const entriesToRestore = Array.from(this.filterSubscriptions.entries()).filter(([, entry]) => entry.restorePending);
|
|
835
|
+
const entriesToRestore = Array.from(this.filterSubscriptions.entries()).filter(([, entry]) => entry.restorePending && !entry.synthetic);
|
|
823
836
|
let failureCount = 0;
|
|
824
837
|
let lastError;
|
|
825
838
|
for (const [hash, entry] of entriesToRestore) {
|
|
@@ -2975,6 +2988,417 @@ function createAsyncQueue() {
|
|
|
2975
2988
|
};
|
|
2976
2989
|
}
|
|
2977
2990
|
|
|
2991
|
+
// src/agent-run-children.ts
|
|
2992
|
+
function spawnBody(o) {
|
|
2993
|
+
return {
|
|
2994
|
+
objective: o.objective,
|
|
2995
|
+
budgets: o.budgets,
|
|
2996
|
+
on_exceeded: o.onExceeded,
|
|
2997
|
+
spawn_key: o.spawnKey,
|
|
2998
|
+
subject: o.subject,
|
|
2999
|
+
as_agent: o.asAgent,
|
|
3000
|
+
agent_ref: o.agentRef,
|
|
3001
|
+
input: o.input ?? null,
|
|
3002
|
+
executor_config: o.executorConfig,
|
|
3003
|
+
reducer: o.reducer,
|
|
3004
|
+
inherit_reducer: o.inheritReducer ?? false
|
|
3005
|
+
};
|
|
3006
|
+
}
|
|
3007
|
+
function checkpointBody(o) {
|
|
3008
|
+
return {
|
|
3009
|
+
fence: o.fence,
|
|
3010
|
+
operation_id: o.operationId,
|
|
3011
|
+
reason: o.reason,
|
|
3012
|
+
summary: o.summary,
|
|
3013
|
+
transcript_cutoff: o.transcriptCutoff,
|
|
3014
|
+
state: o.state,
|
|
3015
|
+
large_refs: o.largeRefs ?? []
|
|
3016
|
+
};
|
|
3017
|
+
}
|
|
3018
|
+
|
|
3019
|
+
// src/agent-runs.ts
|
|
3020
|
+
function createRunBody(options) {
|
|
3021
|
+
return {
|
|
3022
|
+
subject: options.subject,
|
|
3023
|
+
branch: options.branch,
|
|
3024
|
+
agent_ref: options.agentRef,
|
|
3025
|
+
create_key: options.createKey,
|
|
3026
|
+
budgets: options.budgets,
|
|
3027
|
+
input: options.input ?? null,
|
|
3028
|
+
reducer: options.reducer,
|
|
3029
|
+
executor_config: options.executorConfig,
|
|
3030
|
+
control_capability: options.controlCapability,
|
|
3031
|
+
as_agent: options.asAgent
|
|
3032
|
+
};
|
|
3033
|
+
}
|
|
3034
|
+
function subjectOf(subject) {
|
|
3035
|
+
if (typeof subject !== "string") return subject;
|
|
3036
|
+
const cut = subject.indexOf(":/");
|
|
3037
|
+
if (cut <= 0) throw new Error(`subject must be "workspace:/path", got "${subject}"`);
|
|
3038
|
+
return { workspace: subject.slice(0, cut), path: subject.slice(cut + 1) };
|
|
3039
|
+
}
|
|
3040
|
+
var controlCounter = 0;
|
|
3041
|
+
function newControlId(prefix = "ctl") {
|
|
3042
|
+
controlCounter += 1;
|
|
3043
|
+
const rand = Math.random().toString(36).slice(2, 10);
|
|
3044
|
+
return `${prefix}-${Date.now().toString(36)}-${controlCounter}-${rand}`;
|
|
3045
|
+
}
|
|
3046
|
+
var AgentRunsApi = class {
|
|
3047
|
+
constructor(repository, transport) {
|
|
3048
|
+
this.repository = repository;
|
|
3049
|
+
this.transport = transport;
|
|
3050
|
+
}
|
|
3051
|
+
base(runId, rest = "") {
|
|
3052
|
+
const repo = encodeURIComponent(this.repository);
|
|
3053
|
+
const run = runId ? `/${encodeURIComponent(runId)}` : "";
|
|
3054
|
+
return `/api/agent-runs/${repo}${run}${rest}`;
|
|
3055
|
+
}
|
|
3056
|
+
/** Create a run, or get back the subject's live run. */
|
|
3057
|
+
create(options) {
|
|
3058
|
+
return this.transport.request("POST", this.base(), createRunBody(options));
|
|
3059
|
+
}
|
|
3060
|
+
/** Every run about `subject` you may see, newest (the live one) first. */
|
|
3061
|
+
bySubject(subject, limit = 20) {
|
|
3062
|
+
const s = subjectOf(subject);
|
|
3063
|
+
const q = new URLSearchParams({ subject: `${s.workspace}:${s.path ?? ""}`, limit: String(limit) });
|
|
3064
|
+
if (s.node_id) q.set("subject_node_id", s.node_id);
|
|
3065
|
+
return this.transport.request("GET", `${this.base()}?${q.toString()}`);
|
|
3066
|
+
}
|
|
3067
|
+
/** Read a run (record + effective projection). */
|
|
3068
|
+
get(runId) {
|
|
3069
|
+
return this.transport.request("GET", this.base(runId));
|
|
3070
|
+
}
|
|
3071
|
+
/** Your runs in one status (default "running"). */
|
|
3072
|
+
list(status = "running", limit = 50) {
|
|
3073
|
+
return this.transport.request("GET", `${this.base()}?status=${status}&limit=${limit}`);
|
|
3074
|
+
}
|
|
3075
|
+
/** Durable events after `afterSeq` (gap-free; page with the last seq). */
|
|
3076
|
+
events(runId, afterSeq = 0, limit = 500) {
|
|
3077
|
+
return this.transport.request("GET", this.base(runId, `/events?after_seq=${afterSeq}&limit=${limit}`));
|
|
3078
|
+
}
|
|
3079
|
+
/** Any control. Retrying with the same `controlId` is a no-op. */
|
|
3080
|
+
control(runId, command, controlId = newControlId(command.command), capability) {
|
|
3081
|
+
return this.transport.request("POST", this.base(runId, "/control"), {
|
|
3082
|
+
control_id: controlId,
|
|
3083
|
+
command,
|
|
3084
|
+
capability
|
|
3085
|
+
});
|
|
3086
|
+
}
|
|
3087
|
+
stop(runId, reason, controlId) {
|
|
3088
|
+
return this.control(runId, { command: "stop", reason }, controlId);
|
|
3089
|
+
}
|
|
3090
|
+
pause(runId, controlId) {
|
|
3091
|
+
return this.control(runId, { command: "pause" }, controlId);
|
|
3092
|
+
}
|
|
3093
|
+
resume(runId, controlId) {
|
|
3094
|
+
return this.control(runId, { command: "resume" }, controlId);
|
|
3095
|
+
}
|
|
3096
|
+
/** Queue input for the run's next safe boundary. */
|
|
3097
|
+
steer(runId, input, controlId) {
|
|
3098
|
+
return this.control(runId, { command: "steer", input }, controlId);
|
|
3099
|
+
}
|
|
3100
|
+
/** Decide an approval request; the digest ties it to one changeset. */
|
|
3101
|
+
approve(runId, requestId, subjectDigest, reject, controlId) {
|
|
3102
|
+
const decision = reject ? { decision: "reject", reason: reject.reason } : { decision: "approve" };
|
|
3103
|
+
return this.control(
|
|
3104
|
+
runId,
|
|
3105
|
+
{ command: "approve", request_id: requestId, decision, subject_digest: subjectDigest },
|
|
3106
|
+
controlId
|
|
3107
|
+
);
|
|
3108
|
+
}
|
|
3109
|
+
/** Answer an input request. */
|
|
3110
|
+
answer(runId, requestId, value, controlId) {
|
|
3111
|
+
return this.control(runId, { command: "provide_input", request_id: requestId, value }, controlId);
|
|
3112
|
+
}
|
|
3113
|
+
/**
|
|
3114
|
+
* Stream the run's events: replay after `afterSeq`, then live. Ends after
|
|
3115
|
+
* the run's terminal event. Resume after a disconnect with the last `seq`.
|
|
3116
|
+
*/
|
|
3117
|
+
async *stream(runId, afterSeq = 0, signal) {
|
|
3118
|
+
const response = await this.transport.fetch(
|
|
3119
|
+
this.transport.url(this.base(runId, `/stream?after_seq=${afterSeq}`)),
|
|
3120
|
+
{ headers: { ...this.transport.headers(), Accept: "text/event-stream" }, signal }
|
|
3121
|
+
);
|
|
3122
|
+
if (!response.ok || !response.body) {
|
|
3123
|
+
throw new Error(`agent run stream failed: ${response.status} ${await response.text()}`);
|
|
3124
|
+
}
|
|
3125
|
+
const reader = response.body.getReader();
|
|
3126
|
+
const decoder = new TextDecoder();
|
|
3127
|
+
let buffer = "";
|
|
3128
|
+
for (; ; ) {
|
|
3129
|
+
const { value, done } = await reader.read();
|
|
3130
|
+
if (done) return;
|
|
3131
|
+
buffer += decoder.decode(value, { stream: true });
|
|
3132
|
+
let cut;
|
|
3133
|
+
while ((cut = buffer.indexOf("\n\n")) >= 0) {
|
|
3134
|
+
const block = buffer.slice(0, cut);
|
|
3135
|
+
buffer = buffer.slice(cut + 2);
|
|
3136
|
+
let event = "message";
|
|
3137
|
+
let data = "";
|
|
3138
|
+
for (const line of block.split("\n")) {
|
|
3139
|
+
if (line.startsWith("event:")) event = line.slice(6).trim();
|
|
3140
|
+
else if (line.startsWith("data:")) data += line.slice(5).trim();
|
|
3141
|
+
}
|
|
3142
|
+
if (event === "end") return;
|
|
3143
|
+
if (event === "error") throw new Error(`agent run stream error: ${data}`);
|
|
3144
|
+
if (event === "run-event" && data) yield JSON.parse(data);
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
// ---- client-driven runs -------------------------------------------------
|
|
3149
|
+
/** Take the lease of a queued client-driven run. */
|
|
3150
|
+
acquire(runId) {
|
|
3151
|
+
return this.transport.request("POST", this.base(runId, "/lease/acquire"), {});
|
|
3152
|
+
}
|
|
3153
|
+
renew(runId, fence) {
|
|
3154
|
+
return this.transport.request("POST", this.base(runId, "/lease/renew"), { fence });
|
|
3155
|
+
}
|
|
3156
|
+
release(runId, fence) {
|
|
3157
|
+
return this.transport.request("POST", this.base(runId, "/lease/release"), { fence });
|
|
3158
|
+
}
|
|
3159
|
+
/** Begin an operation; returns the active operation (its `op_id`). */
|
|
3160
|
+
begin(runId, fence, op) {
|
|
3161
|
+
return this.transport.request("POST", this.base(runId, "/operations"), { fence, ...op });
|
|
3162
|
+
}
|
|
3163
|
+
/** Record an operation's result. */
|
|
3164
|
+
finish(runId, fence, opId, result) {
|
|
3165
|
+
return this.transport.request("POST", this.base(runId, "/operations/finish"), { fence, op_id: opId, ...result });
|
|
3166
|
+
}
|
|
3167
|
+
/** Open approval/input requests and wait (`kind: "approval" | "input"`). */
|
|
3168
|
+
wait(runId, fence, requests) {
|
|
3169
|
+
return this.transport.request("POST", this.base(runId, "/wait"), { fence, requests });
|
|
3170
|
+
}
|
|
3171
|
+
/** End a client-driven run. */
|
|
3172
|
+
complete(runId, fence, status, outcome) {
|
|
3173
|
+
return this.transport.request("POST", this.base(runId, "/complete"), { fence, status, outcome });
|
|
3174
|
+
}
|
|
3175
|
+
// ── Child runs, mailbox, checkpoints, usage ──────────────────────────────
|
|
3176
|
+
/** Spawn a child with a typed objective; its budgets come out of this run's. */
|
|
3177
|
+
spawnChild(runId, options) {
|
|
3178
|
+
const q = options.branch ? `?branch=${encodeURIComponent(options.branch)}` : "";
|
|
3179
|
+
return this.transport.request("POST", this.base(runId, `/children${q}`), spawnBody(options));
|
|
3180
|
+
}
|
|
3181
|
+
/** Every child of a run: the parent's link plus the child's live status. */
|
|
3182
|
+
children(runId) {
|
|
3183
|
+
return this.transport.request("GET", this.base(runId, "/children"));
|
|
3184
|
+
}
|
|
3185
|
+
/** Inspect one child: record, events after `afterSeq`, usage, checkpoint. */
|
|
3186
|
+
inspectChild(runId, childId, afterSeq = 0, limit = 200) {
|
|
3187
|
+
const rest = `/children/${encodeURIComponent(childId)}?after_seq=${afterSeq}&limit=${limit}`;
|
|
3188
|
+
return this.transport.request("GET", this.base(runId, rest));
|
|
3189
|
+
}
|
|
3190
|
+
/** Message, steer, interrupt or resume a child (idempotent per `controlId`). */
|
|
3191
|
+
controlChild(runId, childId, action, controlId = newControlId(action.action)) {
|
|
3192
|
+
const rest = `/children/${encodeURIComponent(childId)}/control`;
|
|
3193
|
+
return this.transport.request("POST", this.base(runId, rest), { control_id: controlId, ...action });
|
|
3194
|
+
}
|
|
3195
|
+
messageChild(runId, childId, message, controlId) {
|
|
3196
|
+
return this.controlChild(runId, childId, { action: "message", message }, controlId);
|
|
3197
|
+
}
|
|
3198
|
+
steerChild(runId, childId, input, controlId) {
|
|
3199
|
+
return this.controlChild(runId, childId, { action: "steer", input }, controlId);
|
|
3200
|
+
}
|
|
3201
|
+
interruptChild(runId, childId, reason, mode = "stop", controlId) {
|
|
3202
|
+
return this.controlChild(runId, childId, { action: "interrupt", mode, reason }, controlId);
|
|
3203
|
+
}
|
|
3204
|
+
/** A client-driven parent waits (under its lease) for a child's hand-back. */
|
|
3205
|
+
waitChild(runId, childId, fence, expiresAtMs) {
|
|
3206
|
+
const rest = `/children/${encodeURIComponent(childId)}/wait`;
|
|
3207
|
+
return this.transport.request("POST", this.base(runId, rest), { fence, expires_at_ms: expiresAtMs });
|
|
3208
|
+
}
|
|
3209
|
+
/** Unacknowledged mailbox items (hand-backs and child messages). */
|
|
3210
|
+
mailbox(runId) {
|
|
3211
|
+
return this.transport.request("GET", this.base(runId, "/mailbox"));
|
|
3212
|
+
}
|
|
3213
|
+
/** Acknowledge mailbox items up to `upTo`. */
|
|
3214
|
+
ackMailbox(runId, upTo) {
|
|
3215
|
+
return this.transport.request("POST", this.base(runId, "/mailbox/ack"), { up_to: upTo });
|
|
3216
|
+
}
|
|
3217
|
+
/** A child posts a message to its parent's mailbox (idempotent per id). */
|
|
3218
|
+
postToParent(runId, messageId, message) {
|
|
3219
|
+
return this.transport.request("POST", this.base(runId, "/post-to-parent"), { message_id: messageId, message });
|
|
3220
|
+
}
|
|
3221
|
+
/** Write a structured checkpoint (compaction keeps state, not prose). */
|
|
3222
|
+
checkpoint(runId, options) {
|
|
3223
|
+
return this.transport.request("POST", this.base(runId, "/checkpoints"), checkpointBody(options));
|
|
3224
|
+
}
|
|
3225
|
+
/** Read a checkpoint (default: latest) with its structured state. */
|
|
3226
|
+
readCheckpoint(runId, checkpointNo) {
|
|
3227
|
+
return this.transport.request("GET", this.base(runId, `/checkpoints/${checkpointNo ?? "latest"}`));
|
|
3228
|
+
}
|
|
3229
|
+
/** Usage accounting: own, children, reserved, spare. */
|
|
3230
|
+
usage(runId) {
|
|
3231
|
+
return this.transport.request("GET", this.base(runId, "/usage"));
|
|
3232
|
+
}
|
|
3233
|
+
};
|
|
3234
|
+
|
|
3235
|
+
// src/agent-runs-ws.ts
|
|
3236
|
+
var TERMINAL = ["completed", "failed", "stopped"];
|
|
3237
|
+
var AgentRunsWsApi = class {
|
|
3238
|
+
constructor(context, sendRequest, eventHandler) {
|
|
3239
|
+
this.context = context;
|
|
3240
|
+
this.sendRequest = sendRequest;
|
|
3241
|
+
this.eventHandler = eventHandler;
|
|
3242
|
+
}
|
|
3243
|
+
call(type, payload) {
|
|
3244
|
+
return this.sendRequest(payload, type, this.context);
|
|
3245
|
+
}
|
|
3246
|
+
/** Create a run, or get back the subject's live run. */
|
|
3247
|
+
create(options) {
|
|
3248
|
+
return this.call("agent_run_create" /* AgentRunCreate */, createRunBody(options));
|
|
3249
|
+
}
|
|
3250
|
+
/** Read a run (record + effective projection). */
|
|
3251
|
+
get(runId) {
|
|
3252
|
+
return this.call("agent_run_get" /* AgentRunGet */, { run_id: runId });
|
|
3253
|
+
}
|
|
3254
|
+
/**
|
|
3255
|
+
* Every run about `subject` (a conversation, a document) you may see,
|
|
3256
|
+
* newest first — the live run, if there is one, comes first.
|
|
3257
|
+
*/
|
|
3258
|
+
bySubject(subject, limit = 20) {
|
|
3259
|
+
return this.call("agent_run_by_subject" /* AgentRunBySubject */, { ...subjectOf(subject), limit });
|
|
3260
|
+
}
|
|
3261
|
+
/** The newest run of `subject`, or null. */
|
|
3262
|
+
async latest(subject) {
|
|
3263
|
+
const [first] = await this.bySubject(subject, 1);
|
|
3264
|
+
return first ?? null;
|
|
3265
|
+
}
|
|
3266
|
+
/** Your runs in one status (default "running"). */
|
|
3267
|
+
list(status = "running", limit = 50) {
|
|
3268
|
+
return this.call("agent_run_list" /* AgentRunList */, { status, limit });
|
|
3269
|
+
}
|
|
3270
|
+
/** Durable events after `afterSeq`. */
|
|
3271
|
+
events(runId, afterSeq = 0, limit = 500) {
|
|
3272
|
+
return this.call("agent_run_events" /* AgentRunEvents */, { run_id: runId, after_seq: afterSeq, limit });
|
|
3273
|
+
}
|
|
3274
|
+
/** The children a run spawned, with their current status. */
|
|
3275
|
+
children(runId) {
|
|
3276
|
+
return this.call("agent_run_children" /* AgentRunChildren */, { run_id: runId });
|
|
3277
|
+
}
|
|
3278
|
+
/** Any control. Retrying with the same `controlId` is a no-op. */
|
|
3279
|
+
control(runId, command, controlId = newControlId(command.command), capability) {
|
|
3280
|
+
return this.call("agent_run_control" /* AgentRunControl */, { run_id: runId, control_id: controlId, command, capability });
|
|
3281
|
+
}
|
|
3282
|
+
stop(runId, reason, controlId) {
|
|
3283
|
+
return this.control(runId, { command: "stop", reason }, controlId);
|
|
3284
|
+
}
|
|
3285
|
+
pause(runId, controlId) {
|
|
3286
|
+
return this.control(runId, { command: "pause" }, controlId);
|
|
3287
|
+
}
|
|
3288
|
+
resume(runId, controlId) {
|
|
3289
|
+
return this.control(runId, { command: "resume" }, controlId);
|
|
3290
|
+
}
|
|
3291
|
+
/** Queue input for the run's next safe boundary. */
|
|
3292
|
+
steer(runId, input, controlId) {
|
|
3293
|
+
return this.control(runId, { command: "steer", input }, controlId);
|
|
3294
|
+
}
|
|
3295
|
+
/** Decide an approval request; the digest ties it to one changeset. */
|
|
3296
|
+
approve(runId, requestId, subjectDigest, reject, controlId) {
|
|
3297
|
+
const decision = reject ? { decision: "reject", reason: reject.reason } : { decision: "approve" };
|
|
3298
|
+
return this.control(runId, { command: "approve", request_id: requestId, decision, subject_digest: subjectDigest }, controlId);
|
|
3299
|
+
}
|
|
3300
|
+
/** Answer an input request. */
|
|
3301
|
+
answer(runId, requestId, value, controlId) {
|
|
3302
|
+
return this.control(runId, { command: "provide_input", request_id: requestId, value }, controlId);
|
|
3303
|
+
}
|
|
3304
|
+
/**
|
|
3305
|
+
* Follow a run: replay after `afterSeq`, then live, until the run ends.
|
|
3306
|
+
*
|
|
3307
|
+
* Events that reach the socket before the subscription is known here are
|
|
3308
|
+
* not lost: the durable log is read once the listener is in place, live
|
|
3309
|
+
* events are held until that read is merged, and every seq is delivered
|
|
3310
|
+
* exactly once, in order.
|
|
3311
|
+
*/
|
|
3312
|
+
async subscribe(runId, options = {}) {
|
|
3313
|
+
let last = options.afterSeq ?? 0;
|
|
3314
|
+
let ready = false;
|
|
3315
|
+
let closed = false;
|
|
3316
|
+
const held = [];
|
|
3317
|
+
const buffer = [];
|
|
3318
|
+
const waiters = [];
|
|
3319
|
+
let resolveDone;
|
|
3320
|
+
const done = new Promise((r) => resolveDone = r);
|
|
3321
|
+
const close = (ended) => {
|
|
3322
|
+
if (closed) return;
|
|
3323
|
+
closed = true;
|
|
3324
|
+
if (ended) options.onEnd?.(last);
|
|
3325
|
+
for (const w of waiters.splice(0)) w({ value: void 0, done: true });
|
|
3326
|
+
resolveDone();
|
|
3327
|
+
};
|
|
3328
|
+
const deliver = (ev) => {
|
|
3329
|
+
if (closed || ev.seq <= last) return;
|
|
3330
|
+
last = ev.seq;
|
|
3331
|
+
options.onEvent?.(ev);
|
|
3332
|
+
const w = waiters.shift();
|
|
3333
|
+
if (w) w({ value: ev, done: false });
|
|
3334
|
+
else buffer.push(ev);
|
|
3335
|
+
};
|
|
3336
|
+
const listener = (message) => {
|
|
3337
|
+
if (message.event_type === "agent_run_end") {
|
|
3338
|
+
if (ready) close(true);
|
|
3339
|
+
else held.push({ seq: Number.MAX_SAFE_INTEGER });
|
|
3340
|
+
return;
|
|
3341
|
+
}
|
|
3342
|
+
const ev = message.payload;
|
|
3343
|
+
if (ready) deliver(ev);
|
|
3344
|
+
else held.push(ev);
|
|
3345
|
+
};
|
|
3346
|
+
const response = await this.call("agent_run_subscribe" /* AgentRunSubscribe */, {
|
|
3347
|
+
run_id: runId,
|
|
3348
|
+
after_seq: last
|
|
3349
|
+
});
|
|
3350
|
+
const subscriptionId = response.subscription_id;
|
|
3351
|
+
this.eventHandler.addFlowEventListener(subscriptionId, listener);
|
|
3352
|
+
const unsubscribe = async () => {
|
|
3353
|
+
this.eventHandler.removeFlowEventListener(subscriptionId);
|
|
3354
|
+
close(false);
|
|
3355
|
+
await this.call("agent_run_unsubscribe" /* AgentRunUnsubscribe */, { subscription_id: subscriptionId }).catch(() => void 0);
|
|
3356
|
+
};
|
|
3357
|
+
options.signal?.addEventListener("abort", () => void unsubscribe(), { once: true });
|
|
3358
|
+
for (; ; ) {
|
|
3359
|
+
const page = await this.events(runId, last, 500);
|
|
3360
|
+
page.forEach(deliver);
|
|
3361
|
+
if (page.length < 500) break;
|
|
3362
|
+
}
|
|
3363
|
+
ready = true;
|
|
3364
|
+
const endSeen = held.some((e) => e.seq === Number.MAX_SAFE_INTEGER);
|
|
3365
|
+
held.filter((e) => e.seq !== Number.MAX_SAFE_INTEGER).sort((a, b) => a.seq - b.seq).forEach(deliver);
|
|
3366
|
+
if (endSeen) close(true);
|
|
3367
|
+
else {
|
|
3368
|
+
const view = await this.get(runId).catch(() => null);
|
|
3369
|
+
const finalized = !view?.run?.domain || view.run.domain.finalized !== false;
|
|
3370
|
+
if (view && TERMINAL.includes(view.status) && finalized) {
|
|
3371
|
+
const tail = await this.events(runId, last, 500).catch(() => []);
|
|
3372
|
+
tail.forEach(deliver);
|
|
3373
|
+
this.eventHandler.removeFlowEventListener(subscriptionId);
|
|
3374
|
+
close(true);
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
void done.then(() => this.eventHandler.removeFlowEventListener(subscriptionId));
|
|
3378
|
+
return {
|
|
3379
|
+
get lastSeq() {
|
|
3380
|
+
return last;
|
|
3381
|
+
},
|
|
3382
|
+
done,
|
|
3383
|
+
unsubscribe,
|
|
3384
|
+
[Symbol.asyncIterator]() {
|
|
3385
|
+
return {
|
|
3386
|
+
next: () => {
|
|
3387
|
+
const ev = buffer.shift();
|
|
3388
|
+
if (ev) return Promise.resolve({ value: ev, done: false });
|
|
3389
|
+
if (closed) return Promise.resolve({ value: void 0, done: true });
|
|
3390
|
+
return new Promise((r) => waiters.push(r));
|
|
3391
|
+
},
|
|
3392
|
+
return: async () => {
|
|
3393
|
+
await unsubscribe();
|
|
3394
|
+
return { value: void 0, done: true };
|
|
3395
|
+
}
|
|
3396
|
+
};
|
|
3397
|
+
}
|
|
3398
|
+
};
|
|
3399
|
+
}
|
|
3400
|
+
};
|
|
3401
|
+
|
|
2978
3402
|
// src/functions-api.ts
|
|
2979
3403
|
var FunctionsApi = class {
|
|
2980
3404
|
constructor(repository, context, sendRequest) {
|
|
@@ -3165,7 +3589,8 @@ var SearchApi = class {
|
|
|
3165
3589
|
// src/conversations.ts
|
|
3166
3590
|
var DEFAULT_SEND_MESSAGE_INACTIVITY_TIMEOUT_MS = 12e4;
|
|
3167
3591
|
var ConversationManager = class {
|
|
3168
|
-
constructor(baseUrl, repository, authManager, options = {}, executeSql) {
|
|
3592
|
+
constructor(baseUrl, repository, authManager, options = {}, executeSql, visitor) {
|
|
3593
|
+
this.visitor = visitor;
|
|
3169
3594
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
3170
3595
|
this.repository = repository;
|
|
3171
3596
|
this.authManager = authManager;
|
|
@@ -3286,6 +3711,45 @@ var ConversationManager = class {
|
|
|
3286
3711
|
]
|
|
3287
3712
|
};
|
|
3288
3713
|
}
|
|
3714
|
+
/**
|
|
3715
|
+
* Start (or resume) an ANONYMOUS conversation with an agent — a public
|
|
3716
|
+
* website's chat, without login.
|
|
3717
|
+
*
|
|
3718
|
+
* Works on an anonymous connection only, and only with an agent that allows
|
|
3719
|
+
* anonymous visitors (`anonymous.enabled: true` on its raisin:AIAgent). The
|
|
3720
|
+
* server keeps the conversation in an ephemeral visitor session that only
|
|
3721
|
+
* this browser session can read; the session secret is kept in
|
|
3722
|
+
* sessionStorage (override with `options.store`), so a reload continues the
|
|
3723
|
+
* same conversation. Then use `sendMessage`, `getMessages`, `subscribe` and
|
|
3724
|
+
* `ConversationStore` / `useConversation` exactly as for a signed-in user.
|
|
3725
|
+
*
|
|
3726
|
+
* @example
|
|
3727
|
+
* ```typescript
|
|
3728
|
+
* const convo = await db.conversations.startAnonymous('/agents/website-assistant');
|
|
3729
|
+
* for await (const ev of db.conversations.sendMessage(convo.conversationPath, 'Hello')) {
|
|
3730
|
+
* if (ev.type === 'text_chunk') append(ev.text);
|
|
3731
|
+
* if (ev.type === 'failed') showLimit(ev.code); // e.g. RATE_LIMITED, LIMIT_REACHED
|
|
3732
|
+
* }
|
|
3733
|
+
* ```
|
|
3734
|
+
*/
|
|
3735
|
+
async startAnonymous(agent, options) {
|
|
3736
|
+
if (!this.visitor) {
|
|
3737
|
+
throw new Error("Anonymous chat needs a WebSocket database: use client.database(repo).");
|
|
3738
|
+
}
|
|
3739
|
+
return this.visitor.start(this.normalizeAgentPath(agent), options);
|
|
3740
|
+
}
|
|
3741
|
+
/** Forget the stored anonymous session for `agent` (the next start begins a new one). */
|
|
3742
|
+
forgetAnonymous(agent, options) {
|
|
3743
|
+
this.visitor?.forget(this.normalizeAgentPath(agent), options);
|
|
3744
|
+
}
|
|
3745
|
+
/** Whether this tab remembers an anonymous conversation with `agent`. */
|
|
3746
|
+
hasStoredAnonymous(agent, options) {
|
|
3747
|
+
return this.visitor?.remembers(this.normalizeAgentPath(agent), options) ?? false;
|
|
3748
|
+
}
|
|
3749
|
+
/** Whether `conversationPath` is an anonymous conversation of this client. */
|
|
3750
|
+
isAnonymous(conversationPath) {
|
|
3751
|
+
return this.visitor?.isAnonymous(conversationPath) ?? false;
|
|
3752
|
+
}
|
|
3289
3753
|
/**
|
|
3290
3754
|
* Open an existing conversation by path.
|
|
3291
3755
|
* Returns null if not found.
|
|
@@ -3331,6 +3795,7 @@ var ConversationManager = class {
|
|
|
3331
3795
|
* Mark a conversation as read by setting unread_count to 0.
|
|
3332
3796
|
*/
|
|
3333
3797
|
async markAsRead(conversationPath) {
|
|
3798
|
+
if (this.isAnonymous(conversationPath)) return;
|
|
3334
3799
|
const workspace = "raisin:access_control";
|
|
3335
3800
|
await this.sqlQuery(
|
|
3336
3801
|
`UPDATE '${workspace}' SET properties = jsonb_set(properties, '{unread_count}', '0'::jsonb) WHERE path = $1 AND node_type = 'raisin:Conversation'`,
|
|
@@ -3341,6 +3806,7 @@ var ConversationManager = class {
|
|
|
3341
3806
|
* Mark a single message as read by the current user.
|
|
3342
3807
|
*/
|
|
3343
3808
|
async markMessageAsRead(messagePath) {
|
|
3809
|
+
if (messagePath.startsWith("/visitors/")) return;
|
|
3344
3810
|
const workspace = "raisin:access_control";
|
|
3345
3811
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3346
3812
|
await this.sqlQuery(
|
|
@@ -3365,6 +3831,18 @@ var ConversationManager = class {
|
|
|
3365
3831
|
*/
|
|
3366
3832
|
async *sendMessage(conversationPath, content, options) {
|
|
3367
3833
|
const shouldStream = options?.stream !== false;
|
|
3834
|
+
if (this.visitor?.isAnonymous(conversationPath)) {
|
|
3835
|
+
if (!shouldStream) {
|
|
3836
|
+
await this.visitor.post(conversationPath, content);
|
|
3837
|
+
yield { type: "waiting", timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3838
|
+
return;
|
|
3839
|
+
}
|
|
3840
|
+
yield* this.visitor.stream(conversationPath, content, {
|
|
3841
|
+
signal: options?.signal,
|
|
3842
|
+
inactivityTimeoutMs: options?.inactivityTimeoutMs ?? DEFAULT_SEND_MESSAGE_INACTIVITY_TIMEOUT_MS
|
|
3843
|
+
});
|
|
3844
|
+
return;
|
|
3845
|
+
}
|
|
3368
3846
|
logger.debug("[ConversationManager] Starting SSE stream for message send");
|
|
3369
3847
|
if (!shouldStream) {
|
|
3370
3848
|
await this.createUserMessage(conversationPath, content);
|
|
@@ -3452,6 +3930,10 @@ var ConversationManager = class {
|
|
|
3452
3930
|
*/
|
|
3453
3931
|
async createUserMessage(conversationPath, content) {
|
|
3454
3932
|
logger.debug("[ConversationManager] Creating user message", { path: conversationPath });
|
|
3933
|
+
if (this.visitor?.isAnonymous(conversationPath)) {
|
|
3934
|
+
await this.visitor.post(conversationPath, content);
|
|
3935
|
+
return;
|
|
3936
|
+
}
|
|
3455
3937
|
const workspace = "raisin:access_control";
|
|
3456
3938
|
const properties = await this.loadConversationProperties(conversationPath, workspace);
|
|
3457
3939
|
const flowInstanceId = typeof properties?.flow_instance_id === "string" ? properties.flow_instance_id.trim() : "";
|
|
@@ -3503,6 +3985,12 @@ var ConversationManager = class {
|
|
|
3503
3985
|
* initiated messages, etc.). Auto-reconnects on disconnect.
|
|
3504
3986
|
*/
|
|
3505
3987
|
subscribe(conversationPath, onEvent, options) {
|
|
3988
|
+
if (this.visitor?.isAnonymous(conversationPath)) {
|
|
3989
|
+
const stop = this.visitor.listen(conversationPath, onEvent);
|
|
3990
|
+
options?.signal?.addEventListener("abort", stop, { once: true });
|
|
3991
|
+
return { unsubscribe: stop, waitUntilConnected: async () => {
|
|
3992
|
+
} };
|
|
3993
|
+
}
|
|
3506
3994
|
logger.info("[ConversationManager] Subscribing to SSE", { path: conversationPath });
|
|
3507
3995
|
const sseUrl = `${this.baseUrl}/api/conversations/${this.repository}/events`;
|
|
3508
3996
|
const headers = {};
|
|
@@ -3942,6 +4430,272 @@ var ConversationManager = class {
|
|
|
3942
4430
|
}
|
|
3943
4431
|
};
|
|
3944
4432
|
|
|
4433
|
+
// src/visitor-chat.ts
|
|
4434
|
+
function defaultVisitorSessionStore() {
|
|
4435
|
+
const memory = /* @__PURE__ */ new Map();
|
|
4436
|
+
const storage = () => {
|
|
4437
|
+
try {
|
|
4438
|
+
return typeof sessionStorage !== "undefined" ? sessionStorage : null;
|
|
4439
|
+
} catch {
|
|
4440
|
+
return null;
|
|
4441
|
+
}
|
|
4442
|
+
};
|
|
4443
|
+
return {
|
|
4444
|
+
get(key) {
|
|
4445
|
+
try {
|
|
4446
|
+
return storage()?.getItem(key) ?? memory.get(key) ?? null;
|
|
4447
|
+
} catch {
|
|
4448
|
+
return memory.get(key) ?? null;
|
|
4449
|
+
}
|
|
4450
|
+
},
|
|
4451
|
+
set(key, value) {
|
|
4452
|
+
memory.set(key, value);
|
|
4453
|
+
try {
|
|
4454
|
+
storage()?.setItem(key, value);
|
|
4455
|
+
} catch {
|
|
4456
|
+
}
|
|
4457
|
+
},
|
|
4458
|
+
remove(key) {
|
|
4459
|
+
memory.delete(key);
|
|
4460
|
+
try {
|
|
4461
|
+
storage()?.removeItem(key);
|
|
4462
|
+
} catch {
|
|
4463
|
+
}
|
|
4464
|
+
}
|
|
4465
|
+
};
|
|
4466
|
+
}
|
|
4467
|
+
var VisitorChatError = class extends Error {
|
|
4468
|
+
constructor(message, code) {
|
|
4469
|
+
super(message);
|
|
4470
|
+
this.name = "VisitorChatError";
|
|
4471
|
+
this.code = code;
|
|
4472
|
+
}
|
|
4473
|
+
};
|
|
4474
|
+
var REBIND_CODES = /* @__PURE__ */ new Set(["NO_SESSION", "UNKNOWN_CONVERSATION"]);
|
|
4475
|
+
function asError(err) {
|
|
4476
|
+
const e = err;
|
|
4477
|
+
return new VisitorChatError(e?.message ?? String(err), e?.code ?? "UNAVAILABLE");
|
|
4478
|
+
}
|
|
4479
|
+
var VisitorChat = class {
|
|
4480
|
+
constructor(repository, send, events, onReconnected) {
|
|
4481
|
+
this.repository = repository;
|
|
4482
|
+
this.send = send;
|
|
4483
|
+
this.events = events;
|
|
4484
|
+
this.byPath = /* @__PURE__ */ new Map();
|
|
4485
|
+
this.token = null;
|
|
4486
|
+
onReconnected?.(() => {
|
|
4487
|
+
void this.rebindAll();
|
|
4488
|
+
});
|
|
4489
|
+
}
|
|
4490
|
+
/** Whether `conversationPath` is a visitor conversation started here. */
|
|
4491
|
+
isAnonymous(conversationPath) {
|
|
4492
|
+
return this.byPath.has(conversationPath);
|
|
4493
|
+
}
|
|
4494
|
+
storeOf(options) {
|
|
4495
|
+
if (options.store === false) return null;
|
|
4496
|
+
return options.store ?? defaultVisitorSessionStore();
|
|
4497
|
+
}
|
|
4498
|
+
keyOf(agent, options) {
|
|
4499
|
+
return options.storageKey ?? `raisindb:visitor:${this.repository}:${agent}`;
|
|
4500
|
+
}
|
|
4501
|
+
/** Start (or resume) a visitor conversation with `agent` (`/agents/<name>`). */
|
|
4502
|
+
async start(agent, options = {}) {
|
|
4503
|
+
const store = this.storeOf(options);
|
|
4504
|
+
const key = this.keyOf(agent, options);
|
|
4505
|
+
const sessionToken = options.sessionToken ?? this.token ?? store?.get(`${key}:session`) ?? void 0;
|
|
4506
|
+
const conversationId = options.newConversation ? void 0 : options.conversationId ?? store?.get(`${key}:conversation`) ?? void 0;
|
|
4507
|
+
let res;
|
|
4508
|
+
try {
|
|
4509
|
+
res = await this.send(
|
|
4510
|
+
{ agent, session_token: sessionToken, conversation_id: conversationId },
|
|
4511
|
+
"visitor_chat_start"
|
|
4512
|
+
);
|
|
4513
|
+
} catch (err) {
|
|
4514
|
+
const e = asError(err);
|
|
4515
|
+
if ((conversationId || sessionToken) && !options.conversationId && !options.sessionToken && ["UNKNOWN_CONVERSATION", "INVALID_CONVERSATION", "SESSION_EXPIRED", "SESSION_MISMATCH"].includes(e.code)) {
|
|
4516
|
+
store?.remove(`${key}:conversation`);
|
|
4517
|
+
if (e.code !== "UNKNOWN_CONVERSATION" && e.code !== "INVALID_CONVERSATION") {
|
|
4518
|
+
store?.remove(`${key}:session`);
|
|
4519
|
+
this.token = null;
|
|
4520
|
+
}
|
|
4521
|
+
return this.start(agent, { ...options, newConversation: true });
|
|
4522
|
+
}
|
|
4523
|
+
throw e;
|
|
4524
|
+
}
|
|
4525
|
+
const token = res.session_token ?? sessionToken ?? this.token;
|
|
4526
|
+
if (!token) throw new VisitorChatError("the server returned no session", "UNAVAILABLE");
|
|
4527
|
+
this.token = token;
|
|
4528
|
+
store?.set(`${key}:session`, token);
|
|
4529
|
+
store?.set(`${key}:conversation`, res.conversation_id);
|
|
4530
|
+
const path = res.conversation_path;
|
|
4531
|
+
let entry = this.byPath.get(path);
|
|
4532
|
+
if (!entry) {
|
|
4533
|
+
entry = {
|
|
4534
|
+
agent: res.agent,
|
|
4535
|
+
conversationId: res.conversation_id,
|
|
4536
|
+
subscriptionId: res.subscription_id,
|
|
4537
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
4538
|
+
options
|
|
4539
|
+
};
|
|
4540
|
+
this.byPath.set(path, entry);
|
|
4541
|
+
}
|
|
4542
|
+
this.route(entry);
|
|
4543
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4544
|
+
return {
|
|
4545
|
+
id: res.conversation_id,
|
|
4546
|
+
type: "ai_chat",
|
|
4547
|
+
agentRef: res.agent,
|
|
4548
|
+
conversationPath: path,
|
|
4549
|
+
conversationWorkspace: res.conversation_workspace,
|
|
4550
|
+
anonymous: true,
|
|
4551
|
+
conversationId: res.conversation_id,
|
|
4552
|
+
sessionToken: token,
|
|
4553
|
+
limits: res.limits,
|
|
4554
|
+
initialEvents: [{ type: "waiting", timestamp: now }]
|
|
4555
|
+
};
|
|
4556
|
+
}
|
|
4557
|
+
/** Route the server's forwarded events for this conversation to its listeners. */
|
|
4558
|
+
route(entry) {
|
|
4559
|
+
this.events.removeFlowEventListener(entry.subscriptionId);
|
|
4560
|
+
this.events.addFlowEventListener(entry.subscriptionId, (msg) => {
|
|
4561
|
+
const event = msg.payload;
|
|
4562
|
+
if (!event || typeof event.type !== "string") return;
|
|
4563
|
+
for (const listener of entry.listeners) {
|
|
4564
|
+
try {
|
|
4565
|
+
listener(event);
|
|
4566
|
+
} catch (err) {
|
|
4567
|
+
logger.error("[VisitorChat] listener failed", err);
|
|
4568
|
+
}
|
|
4569
|
+
}
|
|
4570
|
+
});
|
|
4571
|
+
}
|
|
4572
|
+
/** Re-prove the session on a new connection, for every conversation started here. */
|
|
4573
|
+
async rebindAll() {
|
|
4574
|
+
for (const entry of this.byPath.values()) {
|
|
4575
|
+
try {
|
|
4576
|
+
await this.send(
|
|
4577
|
+
{ agent: entry.agent, session_token: this.token, conversation_id: entry.conversationId },
|
|
4578
|
+
"visitor_chat_start"
|
|
4579
|
+
);
|
|
4580
|
+
this.route(entry);
|
|
4581
|
+
} catch (err) {
|
|
4582
|
+
logger.warn("[VisitorChat] could not resume a conversation after reconnect", err);
|
|
4583
|
+
}
|
|
4584
|
+
}
|
|
4585
|
+
}
|
|
4586
|
+
/** Listen to a visitor conversation's events. Returns an unsubscribe function. */
|
|
4587
|
+
listen(conversationPath, listener) {
|
|
4588
|
+
const entry = this.require(conversationPath);
|
|
4589
|
+
entry.listeners.add(listener);
|
|
4590
|
+
return () => {
|
|
4591
|
+
entry.listeners.delete(listener);
|
|
4592
|
+
};
|
|
4593
|
+
}
|
|
4594
|
+
require(conversationPath) {
|
|
4595
|
+
const entry = this.byPath.get(conversationPath);
|
|
4596
|
+
if (!entry) {
|
|
4597
|
+
throw new VisitorChatError(
|
|
4598
|
+
"not a visitor conversation of this client; call startAnonymous first",
|
|
4599
|
+
"UNKNOWN_CONVERSATION"
|
|
4600
|
+
);
|
|
4601
|
+
}
|
|
4602
|
+
return entry;
|
|
4603
|
+
}
|
|
4604
|
+
/** Make sure this connection is bound (after a reconnect nothing else may read the home). */
|
|
4605
|
+
async ensureBound(conversationPath) {
|
|
4606
|
+
const entry = this.require(conversationPath);
|
|
4607
|
+
await this.send(
|
|
4608
|
+
{ agent: entry.agent, session_token: this.token, conversation_id: entry.conversationId },
|
|
4609
|
+
"visitor_chat_start"
|
|
4610
|
+
).catch((err) => {
|
|
4611
|
+
throw asError(err);
|
|
4612
|
+
});
|
|
4613
|
+
}
|
|
4614
|
+
/** Send one visitor message. Events arrive through {@link listen}. */
|
|
4615
|
+
async post(conversationPath, text) {
|
|
4616
|
+
const entry = this.require(conversationPath);
|
|
4617
|
+
const once = async () => await this.send(
|
|
4618
|
+
{ conversation_id: entry.conversationId, text },
|
|
4619
|
+
"visitor_chat_send"
|
|
4620
|
+
);
|
|
4621
|
+
try {
|
|
4622
|
+
const res = await once();
|
|
4623
|
+
return { messageId: res.message_id, messagesLeft: res.messages_left };
|
|
4624
|
+
} catch (err) {
|
|
4625
|
+
const e = asError(err);
|
|
4626
|
+
if (!REBIND_CODES.has(e.code)) throw e;
|
|
4627
|
+
await this.ensureBound(conversationPath);
|
|
4628
|
+
this.route(entry);
|
|
4629
|
+
const res = await once().catch((again) => {
|
|
4630
|
+
throw asError(again);
|
|
4631
|
+
});
|
|
4632
|
+
return { messageId: res.message_id, messagesLeft: res.messages_left };
|
|
4633
|
+
}
|
|
4634
|
+
}
|
|
4635
|
+
/**
|
|
4636
|
+
* Send one visitor message and stream the turn: `text_chunk`,
|
|
4637
|
+
* `tool_call_started`, `tool_call_completed`, … until `done`. A refusal
|
|
4638
|
+
* ends the stream with `failed` (its `code` says why). No event for
|
|
4639
|
+
* `inactivityTimeoutMs` ends it with a synthetic `waiting`.
|
|
4640
|
+
*/
|
|
4641
|
+
async *stream(conversationPath, text, options = {}) {
|
|
4642
|
+
const queue = [];
|
|
4643
|
+
let wake = null;
|
|
4644
|
+
const push = (event) => {
|
|
4645
|
+
queue.push(event);
|
|
4646
|
+
wake?.();
|
|
4647
|
+
};
|
|
4648
|
+
const stop = this.listen(conversationPath, push);
|
|
4649
|
+
const onAbort = () => push({ type: "failed", error: "aborted", code: "ABORTED", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
4650
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
4651
|
+
const timeout = options.inactivityTimeoutMs ?? 12e4;
|
|
4652
|
+
try {
|
|
4653
|
+
try {
|
|
4654
|
+
await this.post(conversationPath, text);
|
|
4655
|
+
} catch (err) {
|
|
4656
|
+
const e = asError(err);
|
|
4657
|
+
yield { type: "failed", error: e.message, code: e.code, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4658
|
+
return;
|
|
4659
|
+
}
|
|
4660
|
+
for (; ; ) {
|
|
4661
|
+
if (!queue.length) {
|
|
4662
|
+
const idle = await new Promise((resolve) => {
|
|
4663
|
+
const timer = timeout > 0 ? setTimeout(() => resolve(true), timeout) : null;
|
|
4664
|
+
wake = () => {
|
|
4665
|
+
if (timer) clearTimeout(timer);
|
|
4666
|
+
resolve(false);
|
|
4667
|
+
};
|
|
4668
|
+
});
|
|
4669
|
+
wake = null;
|
|
4670
|
+
if (idle && !queue.length) {
|
|
4671
|
+
yield { type: "waiting", timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4672
|
+
return;
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
const event = queue.shift();
|
|
4676
|
+
yield event;
|
|
4677
|
+
if (event.type === "done" || event.type === "failed" || event.type === "waiting") return;
|
|
4678
|
+
}
|
|
4679
|
+
} finally {
|
|
4680
|
+
stop();
|
|
4681
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
4682
|
+
}
|
|
4683
|
+
}
|
|
4684
|
+
/** Whether the store remembers a conversation with `agent`. */
|
|
4685
|
+
remembers(agent, options = {}) {
|
|
4686
|
+
const store = this.storeOf(options);
|
|
4687
|
+
return !!store?.get(`${this.keyOf(agent, options)}:conversation`);
|
|
4688
|
+
}
|
|
4689
|
+
/** Forget the stored session (e.g. a "new chat" button that should start a new session). */
|
|
4690
|
+
forget(agent, options = {}) {
|
|
4691
|
+
const store = this.storeOf(options);
|
|
4692
|
+
const key = this.keyOf(agent, options);
|
|
4693
|
+
store?.remove(`${key}:session`);
|
|
4694
|
+
store?.remove(`${key}:conversation`);
|
|
4695
|
+
this.token = null;
|
|
4696
|
+
}
|
|
4697
|
+
};
|
|
4698
|
+
|
|
3945
4699
|
// src/inbox.ts
|
|
3946
4700
|
var InboxApi = class {
|
|
3947
4701
|
constructor(baseUrl, repository, authManager, options = {}) {
|
|
@@ -4280,6 +5034,16 @@ var Database = class _Database {
|
|
|
4280
5034
|
}
|
|
4281
5035
|
return this._flowsApi;
|
|
4282
5036
|
}
|
|
5037
|
+
/**
|
|
5038
|
+
* Durable agent runs over this WebSocket connection: find a subject's run,
|
|
5039
|
+
* follow it live (gap-free, resumable), control it, list its children.
|
|
5040
|
+
*/
|
|
5041
|
+
runs() {
|
|
5042
|
+
if (!this._runsApi) {
|
|
5043
|
+
this._runsApi = new AgentRunsWsApi(this._context, this.sendRequest, this.eventHandler);
|
|
5044
|
+
}
|
|
5045
|
+
return this._runsApi;
|
|
5046
|
+
}
|
|
4283
5047
|
/**
|
|
4284
5048
|
* Get FunctionsApi for invoking server-side functions via WebSocket
|
|
4285
5049
|
*/
|
|
@@ -4386,7 +5150,13 @@ var Database = class _Database {
|
|
|
4386
5150
|
this.repository,
|
|
4387
5151
|
this.httpOptions.authManager,
|
|
4388
5152
|
{},
|
|
4389
|
-
(query, params) => this.executeSql(query, params)
|
|
5153
|
+
(query, params) => this.executeSql(query, params),
|
|
5154
|
+
new VisitorChat(
|
|
5155
|
+
this.repository,
|
|
5156
|
+
(payload, requestType) => this.sendRequest(payload, requestType),
|
|
5157
|
+
this.eventHandler,
|
|
5158
|
+
this.httpOptions.onReconnected
|
|
5159
|
+
)
|
|
4390
5160
|
);
|
|
4391
5161
|
}
|
|
4392
5162
|
return this._conversationManager;
|
|
@@ -6016,6 +6786,82 @@ function readTokensFromFragment(hashOrUrl) {
|
|
|
6016
6786
|
return { accessToken, refreshToken: params.get("refresh_token") };
|
|
6017
6787
|
}
|
|
6018
6788
|
|
|
6789
|
+
// src/node-dev.ts
|
|
6790
|
+
var NodeDevApi = class {
|
|
6791
|
+
constructor(repository, transport) {
|
|
6792
|
+
this.repository = repository;
|
|
6793
|
+
this.transport = transport;
|
|
6794
|
+
}
|
|
6795
|
+
/** Call any method by name (the typed helpers below all use this). */
|
|
6796
|
+
call(method, request) {
|
|
6797
|
+
const branch = typeof request.branch === "string" ? `?branch=${encodeURIComponent(request.branch)}` : "";
|
|
6798
|
+
return this.transport.request(
|
|
6799
|
+
"POST",
|
|
6800
|
+
`/api/node-dev/${encodeURIComponent(this.repository)}/${method}${branch}`,
|
|
6801
|
+
request
|
|
6802
|
+
);
|
|
6803
|
+
}
|
|
6804
|
+
changeset(o) {
|
|
6805
|
+
const { idempotencyKey, ...rest } = o;
|
|
6806
|
+
return { ...rest, idempotency_key: idempotencyKey };
|
|
6807
|
+
}
|
|
6808
|
+
stat(target, o = {}) {
|
|
6809
|
+
return this.call("stat", { ...o, target });
|
|
6810
|
+
}
|
|
6811
|
+
read(target, o = {}) {
|
|
6812
|
+
return this.call("read", { ...o, target });
|
|
6813
|
+
}
|
|
6814
|
+
list(target, o = {}) {
|
|
6815
|
+
return this.call("list", { ...o, target });
|
|
6816
|
+
}
|
|
6817
|
+
diff(target, from, to, o = {}) {
|
|
6818
|
+
return this.call("diff", { ...o, target, from, to });
|
|
6819
|
+
}
|
|
6820
|
+
/** Changes inside the roots after `since` (a cursor from a previous call). */
|
|
6821
|
+
watch(since, o = {}) {
|
|
6822
|
+
return this.call("watch", { ...o, since });
|
|
6823
|
+
}
|
|
6824
|
+
dryRun(o) {
|
|
6825
|
+
return this.call("dry_run", this.changeset(o));
|
|
6826
|
+
}
|
|
6827
|
+
propose(o) {
|
|
6828
|
+
return this.call(
|
|
6829
|
+
"propose",
|
|
6830
|
+
this.changeset(o)
|
|
6831
|
+
);
|
|
6832
|
+
}
|
|
6833
|
+
getChangeset(changesetId, o = {}) {
|
|
6834
|
+
return this.call("get_changeset", { ...o, changeset_id: changesetId });
|
|
6835
|
+
}
|
|
6836
|
+
listChangesets(o = {}) {
|
|
6837
|
+
return this.call("list_changesets", { ...o });
|
|
6838
|
+
}
|
|
6839
|
+
/** Commit; pass the reviewed digest to bind the commit to that approval. */
|
|
6840
|
+
commit(changesetId, expectedDigest, o = {}) {
|
|
6841
|
+
return this.call("commit", { ...o, changeset_id: changesetId, expected_digest: expectedDigest });
|
|
6842
|
+
}
|
|
6843
|
+
discard(changesetId, o = {}) {
|
|
6844
|
+
return this.call("discard", { ...o, changeset_id: changesetId });
|
|
6845
|
+
}
|
|
6846
|
+
/** Propose and commit in one call. */
|
|
6847
|
+
apply(o) {
|
|
6848
|
+
return this.call("apply", this.changeset(o));
|
|
6849
|
+
}
|
|
6850
|
+
forkBranch(name, o = {}) {
|
|
6851
|
+
return this.call("fork_branch", { ...o, name });
|
|
6852
|
+
}
|
|
6853
|
+
diffBranch(base = "main", o = {}) {
|
|
6854
|
+
return this.call("diff_branch", { ...o, base });
|
|
6855
|
+
}
|
|
6856
|
+
mergeBranch(target, o = {}) {
|
|
6857
|
+
const { dryRun, ...rest } = o;
|
|
6858
|
+
return this.call("merge_branch", { ...rest, target, dry_run: dryRun });
|
|
6859
|
+
}
|
|
6860
|
+
discardBranch(o = {}) {
|
|
6861
|
+
return this.call("discard_branch", { ...o });
|
|
6862
|
+
}
|
|
6863
|
+
};
|
|
6864
|
+
|
|
6019
6865
|
// src/http-client.ts
|
|
6020
6866
|
var RaisinHttpClient = class extends EventEmitter {
|
|
6021
6867
|
constructor(baseUrl, options = {}) {
|
|
@@ -6741,6 +7587,33 @@ var RaisinHttpClient = class extends EventEmitter {
|
|
|
6741
7587
|
* @param input - Input data passed to the function
|
|
6742
7588
|
* @returns Execution ID and job ID for tracking
|
|
6743
7589
|
*/
|
|
7590
|
+
/**
|
|
7591
|
+
* Durable agent runs of `repository`: create, read, control, stream, and
|
|
7592
|
+
* drive them directly (see {@link AgentRunsApi}).
|
|
7593
|
+
*/
|
|
7594
|
+
agentRuns(repository) {
|
|
7595
|
+
return new AgentRunsApi(repository, {
|
|
7596
|
+
request: async (method, path, body) => (await this.request({ method, path, body })).data,
|
|
7597
|
+
url: (path) => `${this.baseUrl}${path}`,
|
|
7598
|
+
headers: () => {
|
|
7599
|
+
const token = this.authManager.getAccessToken();
|
|
7600
|
+
const headers = {};
|
|
7601
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
7602
|
+
return headers;
|
|
7603
|
+
},
|
|
7604
|
+
fetch: this.fetchImpl
|
|
7605
|
+
});
|
|
7606
|
+
}
|
|
7607
|
+
/**
|
|
7608
|
+
* The node-development surface of `repository`: typed reads, atomic
|
|
7609
|
+
* changesets with review and idempotency, branch worktrees (see
|
|
7610
|
+
* {@link NodeDevApi}).
|
|
7611
|
+
*/
|
|
7612
|
+
nodeDev(repository) {
|
|
7613
|
+
return new NodeDevApi(repository, {
|
|
7614
|
+
request: async (method, path, body) => (await this.request({ method, path, body })).data
|
|
7615
|
+
});
|
|
7616
|
+
}
|
|
6744
7617
|
async invokeFunction(repository, functionName, input, options) {
|
|
6745
7618
|
const requestTimeoutMs = options?.requestTimeoutMs ?? this.options.requestTimeout;
|
|
6746
7619
|
let waitTimeoutMs = options?.waitTimeoutMs;
|
|
@@ -6944,6 +7817,14 @@ var HttpDatabase = class _HttpDatabase {
|
|
|
6944
7817
|
/**
|
|
6945
7818
|
* Get FunctionsApi for invoking server-side functions via HTTP
|
|
6946
7819
|
*/
|
|
7820
|
+
/** Durable agent runs of this repository. */
|
|
7821
|
+
agentRuns() {
|
|
7822
|
+
return this.client.agentRuns(this.repository);
|
|
7823
|
+
}
|
|
7824
|
+
/** The node-development surface of this repository. */
|
|
7825
|
+
nodeDev() {
|
|
7826
|
+
return this.client.nodeDev(this.repository);
|
|
7827
|
+
}
|
|
6947
7828
|
functions() {
|
|
6948
7829
|
return new HttpFunctionsApi(
|
|
6949
7830
|
this.repository,
|
|
@@ -8142,7 +9023,11 @@ var _RaisinClient = class _RaisinClient extends EventEmitter2 {
|
|
|
8142
9023
|
void 0,
|
|
8143
9024
|
() => this.getUploadManager(),
|
|
8144
9025
|
(options) => this.signAssetUrl(options),
|
|
8145
|
-
{
|
|
9026
|
+
{
|
|
9027
|
+
httpBaseUrl: this._httpBaseUrl,
|
|
9028
|
+
authManager: this.authManager,
|
|
9029
|
+
onReconnected: (callback) => this.onReconnected(callback)
|
|
9030
|
+
}
|
|
8146
9031
|
);
|
|
8147
9032
|
}
|
|
8148
9033
|
/**
|
|
@@ -9327,6 +10212,8 @@ function isFlowDefinitionContainer(node) {
|
|
|
9327
10212
|
return node.node_type === "raisin:FlowContainer";
|
|
9328
10213
|
}
|
|
9329
10214
|
export {
|
|
10215
|
+
AgentRunsApi,
|
|
10216
|
+
AgentRunsWsApi,
|
|
9330
10217
|
AllNodeEventTypes,
|
|
9331
10218
|
Archetypes,
|
|
9332
10219
|
AuthManager,
|
|
@@ -9367,6 +10254,7 @@ export {
|
|
|
9367
10254
|
McpClient,
|
|
9368
10255
|
MemoryTokenStorage,
|
|
9369
10256
|
NodeBuilder,
|
|
10257
|
+
NodeDevApi,
|
|
9370
10258
|
NodeEventType,
|
|
9371
10259
|
NodeFileSource,
|
|
9372
10260
|
NodeHelpers,
|
|
@@ -9396,6 +10284,7 @@ export {
|
|
|
9396
10284
|
UploadErrorCode,
|
|
9397
10285
|
UploadManager,
|
|
9398
10286
|
Uploader,
|
|
10287
|
+
VisitorChatError,
|
|
9399
10288
|
WorkspaceClient,
|
|
9400
10289
|
WorkspaceManager,
|
|
9401
10290
|
calculateChunkCount,
|
|
@@ -9417,6 +10306,7 @@ export {
|
|
|
9417
10306
|
createSqlHandler,
|
|
9418
10307
|
createSubscriptionAdapter,
|
|
9419
10308
|
decodeMessage,
|
|
10309
|
+
defaultVisitorSessionStore,
|
|
9420
10310
|
encodeMessage,
|
|
9421
10311
|
getChunkRange,
|
|
9422
10312
|
getContentType,
|
|
@@ -9434,12 +10324,14 @@ export {
|
|
|
9434
10324
|
isTerminalEvent,
|
|
9435
10325
|
isWebSocketClient,
|
|
9436
10326
|
logger,
|
|
10327
|
+
newControlId,
|
|
9437
10328
|
normalizeHomePath,
|
|
9438
10329
|
projectPlansFromMessages,
|
|
9439
10330
|
readTokensFromFragment,
|
|
9440
10331
|
rowsToObjects,
|
|
9441
10332
|
setLogLevel,
|
|
9442
10333
|
sleep,
|
|
10334
|
+
subjectOf,
|
|
9443
10335
|
useConversation,
|
|
9444
10336
|
useConversationList,
|
|
9445
10337
|
useFlow,
|