@jpcasabiancai/cayenne 0.5.2 → 0.5.3

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.
Files changed (2) hide show
  1. package/dist/cli.js +56 -29
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -9925,6 +9925,9 @@ function worthRespawning(current, next) {
9925
9925
  if (next.state !== "ok") return next.state === "none" && current.state !== "none";
9926
9926
  return current.state !== "ok" || current.token !== next.token;
9927
9927
  }
9928
+ function refusedByPostgres(code) {
9929
+ return typeof code === "string" && /^[0-9A-Z]{5}$/.test(code) && code !== UNIQUE_VIOLATION && code !== "40003" && !code.startsWith("08") && !code.startsWith("57P");
9930
+ }
9928
9931
  function assertTaskMode(mode) {
9929
9932
  if (TASK_REFUSED_MODES.has(mode)) {
9930
9933
  throw new Error(`VALIDATION: a task session cannot run in ${mode}`);
@@ -9962,7 +9965,7 @@ function shrink(event) {
9962
9965
  return null;
9963
9966
  }
9964
9967
  }
9965
- var READ_ONLY_TEXT, REFRESH_WITHIN_MS, betweenTurns, UNREADABLE_MARK_CREDS, roundUsd, ORPHANED_STATUSES, RESTARTABLE_STATUSES, isAtOrBelow3, DENY_MESSAGE_MAX, EXIT_ERROR_MAX, INITIAL_PROMPT_MAX, TASK_SKILL_LOOKUP_MS, TASK_REFUSED_MODES, OWNER_DRIVEN_KINDS, REPO_OWNER_ONLY, REPO_NO_DUPLICATE, PROVIDER_SESSION_ID, REPO_WORKTREE_GONE, TASK_ASK_TOOLS, MEMORY_PREAMBLE, MEMORY_SOURCE, wrapMemory, PATH_INPUT_TOOLS, PATH_INPUT_KEYS, isRecord, errorCode, SessionManager;
9968
+ var READ_ONLY_TEXT, REFRESH_WITHIN_MS, betweenTurns, UNREADABLE_MARK_CREDS, roundUsd, UNIQUE_VIOLATION, ROW_WRITE_TIMEOUT_MS, ORPHANED_STATUSES, RESTARTABLE_STATUSES, isAtOrBelow3, DENY_MESSAGE_MAX, EXIT_ERROR_MAX, INITIAL_PROMPT_MAX, TASK_SKILL_LOOKUP_MS, TASK_REFUSED_MODES, OWNER_DRIVEN_KINDS, REPO_OWNER_ONLY, REPO_NO_DUPLICATE, PROVIDER_SESSION_ID, REPO_WORKTREE_GONE, TASK_ASK_TOOLS, MEMORY_PREAMBLE, MEMORY_SOURCE, wrapMemory, PATH_INPUT_TOOLS, PATH_INPUT_KEYS, isRecord, errorCode, SessionManager;
9966
9969
  var init_manager = __esm({
9967
9970
  "src/sessions/manager.ts"() {
9968
9971
  "use strict";
@@ -10004,6 +10007,8 @@ var init_manager = __esm({
10004
10007
  readOnlyText: READ_ONLY_UNAVAILABLE
10005
10008
  };
10006
10009
  roundUsd = (usd) => Math.round(usd * 1e6) / 1e6;
10010
+ UNIQUE_VIOLATION = "23505";
10011
+ ROW_WRITE_TIMEOUT_MS = 3e4;
10007
10012
  ORPHANED_STATUSES = ["running", "awaiting_permission"];
10008
10013
  RESTARTABLE_STATUSES = /* @__PURE__ */ new Set(["idle", ...ORPHANED_STATUSES]);
10009
10014
  isAtOrBelow3 = (p, dir) => p === dir || p.startsWith(dir.endsWith(sep16) ? dir : `${dir}${sep16}`);
@@ -11806,6 +11811,7 @@ var init_manager = __esm({
11806
11811
  stopping: false,
11807
11812
  dropping: null,
11808
11813
  inflight: /* @__PURE__ */ new Set(),
11814
+ writes: Promise.resolve(),
11809
11815
  changesVersion: this.nextChangesSeed(),
11810
11816
  watcher: null,
11811
11817
  docs: null,
@@ -12099,13 +12105,15 @@ var init_manager = __esm({
12099
12105
  );
12100
12106
  session.on("result", (r) => {
12101
12107
  this.deps.timings?.finished(sessionId);
12108
+ const lastSeq = live.writes.then(() => live.seq);
12102
12109
  guard(async () => {
12103
12110
  if (live.provider === "claude") await this.recordCost(sessionId, live, r);
12111
+ const seq = await lastSeq;
12104
12112
  const status = r.isError ? "error" : "idle";
12105
12113
  live.status = status;
12106
12114
  await this.updateSession(sessionId, { status, last_message_at: (/* @__PURE__ */ new Date()).toISOString() });
12107
12115
  const { authMode: _authMode, ...event } = r;
12108
- await this.broadcast(sessionId, { type: "result", seq: live.seq, ...event });
12116
+ await this.broadcast(sessionId, { type: "result", seq, ...event });
12109
12117
  await this.broadcast(sessionId, {
12110
12118
  type: "session.status",
12111
12119
  status,
@@ -12213,36 +12221,55 @@ var init_manager = __esm({
12213
12221
  );
12214
12222
  });
12215
12223
  }
12224
+ /**
12225
+ * Write one `chat_messages` row and broadcast its `message` twin, in this session's write order.
12226
+ *
12227
+ * The step is queued on `live.writes` before this method first awaits, so two calls made back to
12228
+ * back (the SDK hands a short turn's reply and result row over on one tick) insert and broadcast
12229
+ * in call order: insert N and its broadcast both happen before insert N+1 starts. A caller on
12230
+ * the prompt path may therefore wait for earlier rows' inserts — a database wait. It never
12231
+ * waits on a relay send: `broadcast` returns once the relay has the event queued.
12232
+ *
12233
+ * Rejects with `PROVIDER_ERROR: persist failed: …` for this row only; the chain goes on.
12234
+ */
12216
12235
  async persist(sessionId, live, role, content, providerUuid, parentToolUseId, raw) {
12217
12236
  const safeContent = this.secrets.redact(content);
12218
12237
  const safeRaw = this.secrets.redact(raw);
12219
- live.seq += 1;
12220
- const seq = live.seq;
12221
- const { data, error } = await this.deps.db.from("chat_messages").insert({
12222
- session_id: sessionId,
12223
- // Overwritten from the session by stamp_message_workspace; the insert policy still needs
12224
- // is_member(workspace_id) to see a non-null value.
12225
- workspace_id: this.deps.workspaceId,
12226
- seq,
12227
- role,
12228
- content: safeContent,
12229
- provider_uuid: providerUuid,
12230
- parent_tool_use_id: parentToolUseId,
12231
- raw: safeRaw
12232
- }).select(
12233
- "id, session_id, workspace_id, seq, role, content, provider_uuid, parent_tool_use_id, created_at"
12234
- ).single();
12235
- if (error || !data) {
12236
- throw new Error(`PROVIDER_ERROR: persist failed: ${error?.message ?? "no row"}`);
12237
- }
12238
- const row = {
12239
- ...data,
12240
- seq,
12241
- role,
12242
- content: safeContent
12243
- };
12244
- await this.broadcast(sessionId, { type: "message", message: row });
12245
- return row;
12238
+ const step = live.writes.then(async () => {
12239
+ const seq = live.seq + 1;
12240
+ live.seq = seq;
12241
+ const { data, error } = await this.deps.db.from("chat_messages").insert({
12242
+ session_id: sessionId,
12243
+ // Overwritten from the session by stamp_message_workspace; the insert policy still needs
12244
+ // is_member(workspace_id) to see a non-null value.
12245
+ workspace_id: this.deps.workspaceId,
12246
+ seq,
12247
+ role,
12248
+ content: safeContent,
12249
+ provider_uuid: providerUuid,
12250
+ parent_tool_use_id: parentToolUseId,
12251
+ raw: safeRaw
12252
+ }).select(
12253
+ "id, session_id, workspace_id, seq, role, content, provider_uuid, parent_tool_use_id, created_at"
12254
+ ).abortSignal(AbortSignal.timeout(ROW_WRITE_TIMEOUT_MS)).single();
12255
+ if (error || !data) {
12256
+ if (refusedByPostgres(error?.code)) live.seq = seq - 1;
12257
+ throw new Error(`PROVIDER_ERROR: persist failed: ${error?.message ?? "no row"}`);
12258
+ }
12259
+ const row = {
12260
+ ...data,
12261
+ seq,
12262
+ role,
12263
+ content: safeContent
12264
+ };
12265
+ await this.broadcast(sessionId, { type: "message", message: row });
12266
+ return row;
12267
+ });
12268
+ live.writes = step.then(
12269
+ () => void 0,
12270
+ () => void 0
12271
+ );
12272
+ return step;
12246
12273
  }
12247
12274
  /**
12248
12275
  * The highest `chat_messages.seq` this session has, or 0. A failed read is fatal rather than
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jpcasabiancai/cayenne",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "files": [
5
5
  "dist/cli.js"
6
6
  ],