oasis_test 0.1.37 → 0.1.38

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/index.js +159 -45
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -25164,26 +25164,52 @@ async function startOasisServer(opts) {
25164
25164
  ...persistAttachments.length ? { attachments: persistAttachments } : {}
25165
25165
  }).catch(() => void 0);
25166
25166
  }
25167
+ let assistantMsgId;
25168
+ if (chatStore && persistTarget) {
25169
+ const { randomUUID: randomUUID19 } = await import("node:crypto");
25170
+ const id = randomUUID19();
25171
+ await chatStore.appendMessage({
25172
+ id,
25173
+ sessionId: persistTarget.id,
25174
+ role: "assistant",
25175
+ content: "",
25176
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
25177
+ status: "running",
25178
+ ...session.runId ? { runId: session.runId } : {}
25179
+ }).then(() => {
25180
+ assistantMsgId = id;
25181
+ }).catch(() => void 0);
25182
+ }
25167
25183
  let assistantText = "";
25168
25184
  let persisted = false;
25169
25185
  let live = null;
25170
25186
  const localParts = [];
25171
25187
  const collectedParts = () => (live ? live.bufferedParts() : localParts).filter((p2) => p2.type !== "done");
25172
- const finalizeAssistant = async () => {
25188
+ const finalizeAssistant = async (status = "done") => {
25173
25189
  if (persisted || !chatStore || !persistTarget) return;
25174
25190
  persisted = true;
25175
25191
  const parts = collectedParts();
25176
- if (!assistantText && !session.runId && parts.length === 0) return;
25177
- const { randomUUID: randomUUID19 } = await import("node:crypto");
25178
- await chatStore.appendMessage({
25179
- id: randomUUID19(),
25180
- sessionId: persistTarget.id,
25181
- role: "assistant",
25182
- content: assistantText,
25183
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
25184
- ...session.runId ? { runId: session.runId } : {},
25185
- ...parts.length ? { parts } : {}
25186
- }).catch(() => void 0);
25192
+ if (assistantMsgId) {
25193
+ await chatStore.updateMessage(assistantMsgId, {
25194
+ content: assistantText,
25195
+ status,
25196
+ ...parts.length ? { parts } : {},
25197
+ ...session.runId ? { runId: session.runId } : {}
25198
+ }).catch(() => void 0);
25199
+ } else {
25200
+ if (!assistantText && !session.runId && parts.length === 0) return;
25201
+ const { randomUUID: randomUUID19 } = await import("node:crypto");
25202
+ await chatStore.appendMessage({
25203
+ id: randomUUID19(),
25204
+ sessionId: persistTarget.id,
25205
+ role: "assistant",
25206
+ content: assistantText,
25207
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
25208
+ status,
25209
+ ...session.runId ? { runId: session.runId } : {},
25210
+ ...parts.length ? { parts } : {}
25211
+ }).catch(() => void 0);
25212
+ }
25187
25213
  await chatStore.updateSession(persistTarget.id, {
25188
25214
  // 优先落 runtime-native id(codex rollout / ACP session/new 返回),让下一轮 resume
25189
25215
  // 能命中 runtime 认可的 id;claude-code 等接受任意外部 id 的仍回退 session.id 兼容原行为。
@@ -25213,11 +25239,13 @@ async function startOasisServer(opts) {
25213
25239
  }
25214
25240
  }) : null;
25215
25241
  let localSeq = 0;
25242
+ let ckptDirty = false;
25216
25243
  const writeToRes = (part) => {
25217
25244
  if (!res.destroyed) res.write(`${JSON.stringify(part)}
25218
25245
  `);
25219
25246
  };
25220
25247
  const emit = (part) => {
25248
+ ckptDirty = true;
25221
25249
  if (live) return live.emit(part);
25222
25250
  const withSeq = { ...part, seq: ++localSeq };
25223
25251
  if (part.type !== "done") localParts.push(withSeq);
@@ -25233,6 +25261,13 @@ async function startOasisServer(opts) {
25233
25261
  });
25234
25262
  res.flushHeaders();
25235
25263
  stopKeepalive = startStreamKeepalive(res);
25264
+ const ckptTimer = setInterval(() => {
25265
+ if (!ckptDirty || !assistantMsgId || !chatStore) return;
25266
+ ckptDirty = false;
25267
+ const parts = collectedParts();
25268
+ void chatStore.updateMessage(assistantMsgId, { content: assistantText, status: "running", ...parts.length ? { parts } : {} }).catch(() => void 0);
25269
+ }, 2e3);
25270
+ ckptTimer.unref?.();
25236
25271
  if (live) detachViewer = live.subscribe(writeToRes);
25237
25272
  let sawTextOutput = false;
25238
25273
  session.onOutput((chunk) => {
@@ -25253,7 +25288,8 @@ async function startOasisServer(opts) {
25253
25288
  emit({ type: "error", text: err instanceof Error ? err.message : String(err) });
25254
25289
  }
25255
25290
  finished = true;
25256
- await finalizeAssistant();
25291
+ clearInterval(ckptTimer);
25292
+ await finalizeAssistant(errored ? "error" : "done");
25257
25293
  emit({ type: "done", sessionId: session.id });
25258
25294
  live?.finish(errored ? "error" : "done");
25259
25295
  stopKeepalive?.();
@@ -28896,6 +28932,17 @@ var init_dev_store = __esm({
28896
28932
  this.messages.set(m2.sessionId, list);
28897
28933
  return record5;
28898
28934
  }
28935
+ async updateMessage(id, patch) {
28936
+ for (const list of this.messages.values()) {
28937
+ const item = list.find((x2) => x2.id === id);
28938
+ if (!item) continue;
28939
+ if (patch.content !== void 0) item.content = patch.content;
28940
+ if (patch.parts !== void 0) item.parts = patch.parts;
28941
+ if (patch.status !== void 0) item.status = patch.status;
28942
+ if (patch.runId !== void 0) item.runId = patch.runId;
28943
+ return;
28944
+ }
28945
+ }
28899
28946
  async listMessages(sessionId, limit) {
28900
28947
  const list = (this.messages.get(sessionId) ?? []).sort((a, b2) => a.seq - b2.seq);
28901
28948
  return limit ? list.slice(-limit) : list;
@@ -28944,6 +28991,10 @@ var init_dev_store = __esm({
28944
28991
  this.save();
28945
28992
  return record5;
28946
28993
  }
28994
+ async updateMessage(id, patch) {
28995
+ await super.updateMessage(id, patch);
28996
+ this.save();
28997
+ }
28947
28998
  async linkWorkOrder(sessionId, workOrderId) {
28948
28999
  await super.linkWorkOrder(sessionId, workOrderId);
28949
29000
  this.save();
@@ -37926,6 +37977,31 @@ function codexSessionsRoot() {
37926
37977
  }
37927
37978
  return "";
37928
37979
  }
37980
+ function codexRolloutExists(sessionId, root = codexSessionsRoot()) {
37981
+ if (!sessionId || !root) return false;
37982
+ const suffix = `-${sessionId}.jsonl`;
37983
+ let hit = false;
37984
+ const walk = (d) => {
37985
+ if (hit) return;
37986
+ let ents;
37987
+ try {
37988
+ ents = fs8.readdirSync(d, { withFileTypes: true });
37989
+ } catch {
37990
+ return;
37991
+ }
37992
+ for (const ent of ents) {
37993
+ if (hit) return;
37994
+ const p2 = path8.join(d, ent.name);
37995
+ if (ent.isDirectory()) walk(p2);
37996
+ else if (ent.isFile() && ent.name.startsWith("rollout-") && ent.name.endsWith(suffix)) {
37997
+ hit = true;
37998
+ return;
37999
+ }
38000
+ }
38001
+ };
38002
+ walk(root);
38003
+ return hit;
38004
+ }
37929
38005
  function listRecentRollouts(root, sinceMs) {
37930
38006
  const margin = sinceMs - 6e4;
37931
38007
  const found = [];
@@ -38176,7 +38252,9 @@ var init_codex = __esm({
38176
38252
  const task = job.bundle.files["TASK.md"];
38177
38253
  if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
38178
38254
  const prompt = job.systemPrompt ? ["# System Prompt", job.systemPrompt, "", "# Task", task].join("\n") : task;
38179
- const isResume = Boolean(job.resumeRuntimeSession && job.runtimeSessionId);
38255
+ const isResume = Boolean(
38256
+ job.resumeRuntimeSession && job.runtimeSessionId && codexRolloutExists(job.runtimeSessionId)
38257
+ );
38180
38258
  const args = [
38181
38259
  "exec",
38182
38260
  "-C",
@@ -40216,39 +40294,47 @@ function nodesDomain(deps) {
40216
40294
  if (msg.type !== "hello") return;
40217
40295
  const d = hub.connectedDaemons().find((c) => c.daemonId === daemonId);
40218
40296
  if (!d) return;
40219
- if (msg.meta.runtimes) {
40220
- const existing = await nodeStore.getNode(daemonId);
40221
- await nodeStore.upsertNode({
40222
- id: daemonId,
40223
- hostname: d.meta.hostname,
40224
- nodeVersion: d.meta.nodeVersion ?? "unknown",
40225
- status: "online",
40226
- // revives a previously soft-deleted node
40227
- lastSeenAt: now(),
40228
- enrolledAt: existing?.enrolledAt ?? now(),
40229
- ...d.meta.nodeName ? { name: d.meta.nodeName } : existing?.name ? { name: existing.name } : {}
40230
- });
40231
- for (const cap of msg.meta.runtimes) {
40232
- await nodeStore.addRuntimeIfAbsent({
40233
- id: `runtime:${daemonId}:${cap.kind}`,
40234
- nodeId: daemonId,
40235
- kind: cap.kind,
40297
+ try {
40298
+ if (msg.meta.runtimes) {
40299
+ const existing = await nodeStore.getNode(daemonId);
40300
+ await nodeStore.upsertNode({
40301
+ id: daemonId,
40236
40302
  hostname: d.meta.hostname,
40237
- ...cap.binary ? { binary: cap.binary } : {},
40238
- ...cap.version ? { version: cap.version } : {},
40303
+ nodeVersion: d.meta.nodeVersion ?? "unknown",
40239
40304
  status: "online",
40240
- lastSeenAt: now()
40305
+ // revives a previously soft-deleted node
40306
+ lastSeenAt: now(),
40307
+ enrolledAt: existing?.enrolledAt ?? now(),
40308
+ ...d.meta.nodeName ? { name: d.meta.nodeName } : existing?.name ? { name: existing.name } : {}
40241
40309
  });
40310
+ for (const cap of msg.meta.runtimes) {
40311
+ await nodeStore.addRuntimeIfAbsent({
40312
+ id: `runtime:${daemonId}:${cap.kind}`,
40313
+ nodeId: daemonId,
40314
+ kind: cap.kind,
40315
+ hostname: d.meta.hostname,
40316
+ ...cap.binary ? { binary: cap.binary } : {},
40317
+ ...cap.version ? { version: cap.version } : {},
40318
+ status: "online",
40319
+ lastSeenAt: now()
40320
+ });
40321
+ }
40322
+ await nodeStore.setNodeRuntimesOnline(daemonId, true);
40323
+ } else {
40324
+ await nodeStore.setNodeOnline(daemonId, true);
40325
+ await nodeStore.setNodeRuntimesOnline(daemonId, true);
40242
40326
  }
40243
- await nodeStore.setNodeRuntimesOnline(daemonId, true);
40244
- } else {
40245
- await nodeStore.setNodeOnline(daemonId, true);
40246
- await nodeStore.setNodeRuntimesOnline(daemonId, true);
40327
+ } catch (err) {
40328
+ console.warn(`[nodes] hello \u5904\u7406\u5931\u8D25(node=${daemonId})\uFF1A${err instanceof Error ? err.message : String(err)}`);
40247
40329
  }
40248
40330
  });
40249
40331
  hub.onDaemonDisconnected = async (daemonId) => {
40250
- await nodeStore.setNodeOnline(daemonId, false);
40251
- await nodeStore.setNodeRuntimesOnline(daemonId, false);
40332
+ try {
40333
+ await nodeStore.setNodeOnline(daemonId, false);
40334
+ await nodeStore.setNodeRuntimesOnline(daemonId, false);
40335
+ } catch (err) {
40336
+ console.warn(`[nodes] disconnect \u5904\u7406\u5931\u8D25(node=${daemonId})\uFF1A${err instanceof Error ? err.message : String(err)}`);
40337
+ }
40252
40338
  };
40253
40339
  }
40254
40340
  return (router) => {
@@ -54232,6 +54318,7 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
54232
54318
  await pool.query(`ALTER TABLE "${s2}".chat_messages ADD COLUMN IF NOT EXISTS run_id text`);
54233
54319
  await pool.query(`ALTER TABLE "${s2}".chat_messages ADD COLUMN IF NOT EXISTS parts jsonb`);
54234
54320
  await pool.query(`ALTER TABLE "${s2}".chat_messages ADD COLUMN IF NOT EXISTS attachments jsonb`);
54321
+ await pool.query(`ALTER TABLE "${s2}".chat_messages ADD COLUMN IF NOT EXISTS status text`);
54235
54322
  await pool.query(`ALTER TABLE "${s2}".chat_sessions ADD COLUMN IF NOT EXISTS workspace text`);
54236
54323
  await pool.query(`
54237
54324
  WITH ranked AS (
@@ -54297,11 +54384,11 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
54297
54384
  async appendMessage(m2) {
54298
54385
  const insert = async () => {
54299
54386
  const r = await this.pool.query(
54300
- `INSERT INTO ${this.s}.chat_messages (id, session_id, role, content, seq, created_at, run_id, parts, attachments)
54301
- SELECT $1,$2,$3,$4, COALESCE(MAX(seq),0)+1, $5,$6,$7,$8
54387
+ `INSERT INTO ${this.s}.chat_messages (id, session_id, role, content, seq, created_at, run_id, parts, attachments, status)
54388
+ SELECT $1,$2,$3,$4, COALESCE(MAX(seq),0)+1, $5,$6,$7,$8,$9
54302
54389
  FROM ${this.s}.chat_messages WHERE session_id = $2
54303
54390
  RETURNING seq`,
54304
- [m2.id, m2.sessionId, m2.role, m2.content, m2.createdAt, m2.runId ?? null, m2.parts !== void 0 ? JSON.stringify(m2.parts) : null, m2.attachments !== void 0 ? JSON.stringify(m2.attachments) : null]
54391
+ [m2.id, m2.sessionId, m2.role, m2.content, m2.createdAt, m2.runId ?? null, m2.parts !== void 0 ? JSON.stringify(m2.parts) : null, m2.attachments !== void 0 ? JSON.stringify(m2.attachments) : null, m2.status ?? null]
54305
54392
  );
54306
54393
  return Number(r.rows[0].seq);
54307
54394
  };
@@ -54315,6 +54402,29 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
54315
54402
  }
54316
54403
  }
54317
54404
  }
54405
+ async updateMessage(id, patch) {
54406
+ const sets = [];
54407
+ const args = [];
54408
+ if (patch.content !== void 0) {
54409
+ args.push(patch.content);
54410
+ sets.push(`content = $${args.length}`);
54411
+ }
54412
+ if (patch.parts !== void 0) {
54413
+ args.push(JSON.stringify(patch.parts));
54414
+ sets.push(`parts = $${args.length}`);
54415
+ }
54416
+ if (patch.status !== void 0) {
54417
+ args.push(patch.status);
54418
+ sets.push(`status = $${args.length}`);
54419
+ }
54420
+ if (patch.runId !== void 0) {
54421
+ args.push(patch.runId);
54422
+ sets.push(`run_id = $${args.length}`);
54423
+ }
54424
+ if (sets.length === 0) return;
54425
+ args.push(id);
54426
+ await this.pool.query(`UPDATE ${this.s}.chat_messages SET ${sets.join(", ")} WHERE id = $${args.length}`, args);
54427
+ }
54318
54428
  async listMessages(sessionId, limit) {
54319
54429
  const limitClause = limit ? `LIMIT ${limit}` : "";
54320
54430
  const r = await this.pool.query(
@@ -54358,7 +54468,8 @@ var rowToMessage = (row) => ({
54358
54468
  createdAt: new Date(row.created_at).toISOString(),
54359
54469
  ...row.run_id != null ? { runId: row.run_id } : {},
54360
54470
  ...row.parts != null ? { parts: row.parts } : {},
54361
- ...row.attachments != null ? { attachments: row.attachments } : {}
54471
+ ...row.attachments != null ? { attachments: row.attachments } : {},
54472
+ ...row.status != null ? { status: row.status } : {}
54362
54473
  });
54363
54474
 
54364
54475
  // ../storage/src/postgres-nodes.ts
@@ -55062,6 +55173,9 @@ async function startServe(opts) {
55062
55173
  return service.listSkillFiles(skillId);
55063
55174
  }
55064
55175
  });
55176
+ process.on("unhandledRejection", (reason) => {
55177
+ console.error(`[serve] unhandledRejection\uFF08\u5DF2\u515C\u5E95\uFF0C\u4E0D\u5D29\u8FDB\u7A0B\uFF09\uFF1A${reason instanceof Error ? reason.stack ?? reason.message : String(reason)}`);
55178
+ });
55065
55179
  const server = await startOasisServer({
55066
55180
  kernel: engine.kernel,
55067
55181
  blobs: engine.blobs,
@@ -58005,7 +58119,7 @@ ${res.warning}`);
58005
58119
  }
58006
58120
 
58007
58121
  // src/index.ts
58008
- var PKG_VERSION = true ? "0.1.37" : "dev";
58122
+ var PKG_VERSION = true ? "0.1.38" : "dev";
58009
58123
  var OASIS_DIR = path18.join(os10.homedir(), ".oasis");
58010
58124
  var CONFIG_FILE = path18.join(OASIS_DIR, "node-config.json");
58011
58125
  var PID_FILE = path18.join(OASIS_DIR, "node.pid");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test",
3
- "version": "0.1.37",
3
+ "version": "0.1.38",
4
4
  "description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
5
5
  "bin": {
6
6
  "oasis": "./dist/index.js"