oasis_test 0.1.32 → 0.1.34

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 +276 -116
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -387,6 +387,8 @@ function fold(model, op) {
387
387
  head: artifact.currentRev,
388
388
  at: op.timestamp,
389
389
  seq: op.seq,
390
+ by: op.actor,
391
+ note: p2.note,
390
392
  gate: p2.gate ? { quorum: p2.gate.quorum, eligible: [...p2.gate.eligible] } : null
391
393
  });
392
394
  break;
@@ -404,6 +406,7 @@ function fold(model, op) {
404
406
  head: artifact.currentRev,
405
407
  at: op.timestamp,
406
408
  seq: op.seq,
409
+ by: op.actor,
407
410
  gate: null,
408
411
  forced: { by: op.actor, reason: p2.reason }
409
412
  });
@@ -3795,6 +3798,7 @@ function computeProduceJobs(model) {
3795
3798
  out.push({ artifactId: artifact.id });
3796
3799
  continue;
3797
3800
  }
3801
+ if (partHasOpenBackpressure(model, artifact.id, null)) continue;
3798
3802
  for (const part of parts) {
3799
3803
  if (gaps.some((g2) => g2.part === part.name) || partHasOpenBackpressure(model, artifact.id, part.name)) continue;
3800
3804
  if (partProduced(model, artifact.id, part.name) && !partReworkNeeded(model, artifact.id, part.name)) continue;
@@ -23535,9 +23539,20 @@ async function resolveContent(blobs, args) {
23535
23539
  throw new Error("propose \u9700\u8981 content\uFF08\u5355\u6587\u4EF6\uFF09/ files\uFF08\u591A\u6587\u4EF6\uFF09/ externalPin\uFF08\u5916\u90E8\u5927\u4EF6\uFF09\u4E4B\u4E00");
23536
23540
  }
23537
23541
  async function resolveProposeContent(kernel, blobs, args, artifactId, ctx) {
23538
- const explicit = typeof args["content"] === "string" || Array.isArray(args["files"]) || optStr(args, "externalPin") !== void 0;
23539
- if (!explicit && ctx.resolveCodeRepo) {
23540
- const ws = kernel.model.artifacts.get(artifactId)?.workspace;
23542
+ const hasInline = typeof args["content"] === "string" || Array.isArray(args["files"]);
23543
+ const pin = optStr(args, "externalPin");
23544
+ const art = kernel.model.artifacts.get(artifactId);
23545
+ const typeDef = art ? ctx.schema?.find((t) => t.name === art.type) : void 0;
23546
+ const isCode = typeDef?.contentType === "code";
23547
+ const knownNonCode = typeDef !== void 0 && !isCode;
23548
+ if (isCode && hasInline) {
23549
+ throw new Error(
23550
+ "\u4EE3\u7801\u8282\u70B9\u7684\u5185\u5BB9\u5728 git\uFF0C\u4E0D\u63A5\u53D7 --file/--from-dir \u5185\u8054\u5185\u5BB9\u3002\u8BF7\u5728\u4F60\u6539\u8FC7\u7684\u4ED3\u5E93 commit \u5E76 `git push` \u5230\u5DE5\u5355 feat \u5206\u652F\u540E\uFF0C\u76F4\u63A5 `oasis propose <id> --reason`\uFF08\u7CFB\u7EDF\u81EA\u52A8\u4ECE git \u6536\u96C6\uFF09\uFF0C\u6216\u7528 `oasis propose <id> --git-commit <sha>` \u624B\u52A8\u6307\u5B9A\u90A3\u6B21\u63D0\u4EA4\u3002"
23551
+ );
23552
+ }
23553
+ if (pin !== void 0) return { contentRef: pin, contentKind: "external-pin" };
23554
+ if (!hasInline && !knownNonCode && ctx.resolveCodeRepo) {
23555
+ const ws = art?.workspace;
23541
23556
  const repos = ws ? (await ctx.resolveCodeRepo(ws))?.repos : void 0;
23542
23557
  if (repos?.length) {
23543
23558
  const slug6 = (id) => id.replace(/[^a-zA-Z0-9_-]+/g, "_");
@@ -23549,6 +23564,14 @@ async function resolveProposeContent(kernel, blobs, args, artifactId, ctx) {
23549
23564
  }
23550
23565
  return { contentRef: JSON.stringify(commits), contentKind: "external-pin" };
23551
23566
  }
23567
+ if (isCode) {
23568
+ throw new Error(
23569
+ '\u4EE3\u7801\u8282\u70B9\u4F46\u672C\u5DE5\u5355\u672A\u914D\u7F6E\u4EE3\u7801\u4ED3\u5E93\uFF0C\u6536\u96C6\u4E0D\u5230 git \u5185\u5BB9\u3002\u8BF7\u7528 `oasis gap <id> --desc "\u7F3A\u4EE3\u7801\u4ED3\u5E93\u914D\u7F6E"` \u4E0A\u62A5\uFF0C\u522B\u7528 --file \u515C\u5E95\u3002'
23570
+ );
23571
+ }
23572
+ }
23573
+ if (!hasInline && knownNonCode) {
23574
+ throw new Error("\u9700\u8981\u5185\u5BB9\uFF1A`--file <\u6587\u4EF6>`\uFF08\u5355\u6587\u4EF6\uFF09\u6216 `--from-dir <\u76EE\u5F55>`\uFF08\u591A\u6587\u4EF6\uFF09\u3002");
23552
23575
  }
23553
23576
  return resolveContent(blobs, args);
23554
23577
  }
@@ -24975,7 +24998,9 @@ async function startOasisServer(opts) {
24975
24998
  ...streamedParts.length ? { parts: streamedParts } : {}
24976
24999
  }).catch(() => void 0);
24977
25000
  await chatStore.updateSession(persistTarget.id, {
24978
- runtimeSessionId: session.id,
25001
+ // 优先落 runtime-native id(codex rollout / ACP session/new 返回),让下一轮 resume
25002
+ // 能命中 runtime 认可的 id;claude-code 等接受任意外部 id 的仍回退 session.id 兼容原行为。
25003
+ runtimeSessionId: session.nativeSessionId ?? session.id,
24979
25004
  touchedAt: (/* @__PURE__ */ new Date()).toISOString()
24980
25005
  }).catch(() => void 0);
24981
25006
  };
@@ -34164,6 +34189,94 @@ var init_image_upload = __esm({
34164
34189
  }
34165
34190
  });
34166
34191
 
34192
+ // ../server/src/domains/actors/skill-materializer.ts
34193
+ async function listSkillMetadataForActor(args) {
34194
+ const skills = await args.service.listInstalledSkillsForActor(args.actorId);
34195
+ if (skills.length === 0) return [];
34196
+ const listMeta = args.service.listSkillFilesMeta;
34197
+ if (!listMeta) {
34198
+ throw new Error("SkillFileSource.listSkillFilesMeta \u672A\u5B9E\u73B0\u2014\u2014\u6280\u80FD\u589E\u91CF\u7F13\u5B58\u9700\u8981\u5B83");
34199
+ }
34200
+ const out = [];
34201
+ const usedDirs = /* @__PURE__ */ new Set();
34202
+ for (const skill of skills) {
34203
+ const metas = await listMeta.call(args.service, skill.id);
34204
+ if (metas.length === 0) continue;
34205
+ let dir = sanitizeSkillDir(skill.name);
34206
+ if (usedDirs.has(dir)) {
34207
+ let n = 2;
34208
+ while (usedDirs.has(`${dir}-${n}`)) n++;
34209
+ dir = `${dir}-${n}`;
34210
+ }
34211
+ usedDirs.add(dir);
34212
+ const sorted = [...metas].sort((a, b2) => a.path.localeCompare(b2.path));
34213
+ let latest = "1970-01-01T00:00:00.000Z";
34214
+ const hasher = (0, import_node_crypto7.createHash)("sha256");
34215
+ for (const m2 of sorted) {
34216
+ if (m2.updatedAt > latest) latest = m2.updatedAt;
34217
+ hasher.update(m2.path);
34218
+ hasher.update("\0");
34219
+ hasher.update(m2.blobHash);
34220
+ hasher.update("\0");
34221
+ }
34222
+ out.push({
34223
+ id: skill.id,
34224
+ name: skill.name,
34225
+ dir,
34226
+ updatedAt: latest,
34227
+ contentHash: `sha256:${hasher.digest("hex")}`,
34228
+ fileList: sorted.map((m2) => m2.path)
34229
+ });
34230
+ }
34231
+ return out;
34232
+ }
34233
+ function skillsDirForRuntime(runtimeKind) {
34234
+ switch (runtimeKind) {
34235
+ case "claude":
34236
+ case "claude-code":
34237
+ return ".claude/skills";
34238
+ default:
34239
+ return ".agent_context/skills";
34240
+ }
34241
+ }
34242
+ function sanitizeSkillDir(name) {
34243
+ const s2 = name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
34244
+ return s2 || "skill";
34245
+ }
34246
+ async function materializeSkillFiles(args) {
34247
+ const skills = await args.service.listInstalledSkillsForActor(args.actorId);
34248
+ if (skills.length === 0) return {};
34249
+ const prefix = skillsDirForRuntime(args.runtimeKind);
34250
+ const out = {};
34251
+ const usedDirs = /* @__PURE__ */ new Set();
34252
+ for (const skill of skills) {
34253
+ const files = await args.service.listSkillFiles(skill.id);
34254
+ if (files.length === 0) continue;
34255
+ let dir = sanitizeSkillDir(skill.name);
34256
+ if (usedDirs.has(dir)) {
34257
+ let n = 2;
34258
+ while (usedDirs.has(`${dir}-${n}`)) n++;
34259
+ dir = `${dir}-${n}`;
34260
+ }
34261
+ usedDirs.add(dir);
34262
+ for (const f2 of files) {
34263
+ if (!f2.content) {
34264
+ console.warn(`[skill-materializer] skip ${skill.id}/${f2.path}\uFF1Acontent \u4E3A\u7A7A\uFF08\u672A\u56DE\u586B\uFF1F\uFF09\uFF0C\u4E0D\u5199\u7A7A\u6280\u80FD\u6587\u4EF6`);
34265
+ continue;
34266
+ }
34267
+ out[`${prefix}/${dir}/${f2.path}`] = f2.content;
34268
+ }
34269
+ }
34270
+ return out;
34271
+ }
34272
+ var import_node_crypto7;
34273
+ var init_skill_materializer = __esm({
34274
+ "../server/src/domains/actors/skill-materializer.ts"() {
34275
+ "use strict";
34276
+ import_node_crypto7 = require("node:crypto");
34277
+ }
34278
+ });
34279
+
34167
34280
  // ../server/src/domains/actors/routes.ts
34168
34281
  function positionsFromPayload(body) {
34169
34282
  if (!body) return void 0;
@@ -34554,6 +34667,16 @@ function actorsDomain(opts) {
34554
34667
  await service.deleteSkillFile(req.params.id, req.params["*"] ?? "");
34555
34668
  return { status: 200, body: { ok: true } };
34556
34669
  });
34670
+ router.get("/api/skill-cache/manifest", async (req) => {
34671
+ const { service } = await resolveCtx(req.auth.companyId);
34672
+ const items = await listSkillMetadataForActor({ service, actorId: req.auth.actor });
34673
+ return { status: 200, body: { items } };
34674
+ });
34675
+ router.get("/api/skill-cache/files/:skillId", async (req) => {
34676
+ const { service } = await resolveCtx(req.auth.companyId);
34677
+ const files = await service.listSkillFiles(req.params.skillId);
34678
+ return { status: 200, body: { files } };
34679
+ });
34557
34680
  };
34558
34681
  }
34559
34682
  var init_routes = __esm({
@@ -34564,94 +34687,7 @@ var init_routes = __esm({
34564
34687
  init_stats();
34565
34688
  init_activity();
34566
34689
  init_image_upload();
34567
- }
34568
- });
34569
-
34570
- // ../server/src/domains/actors/skill-materializer.ts
34571
- async function listSkillMetadataForActor(args) {
34572
- const skills = await args.service.listInstalledSkillsForActor(args.actorId);
34573
- if (skills.length === 0) return [];
34574
- const listMeta = args.service.listSkillFilesMeta;
34575
- if (!listMeta) {
34576
- throw new Error("SkillFileSource.listSkillFilesMeta \u672A\u5B9E\u73B0\u2014\u2014\u6280\u80FD\u589E\u91CF\u7F13\u5B58\u9700\u8981\u5B83");
34577
- }
34578
- const out = [];
34579
- const usedDirs = /* @__PURE__ */ new Set();
34580
- for (const skill of skills) {
34581
- const metas = await listMeta.call(args.service, skill.id);
34582
- if (metas.length === 0) continue;
34583
- let dir = sanitizeSkillDir(skill.name);
34584
- if (usedDirs.has(dir)) {
34585
- let n = 2;
34586
- while (usedDirs.has(`${dir}-${n}`)) n++;
34587
- dir = `${dir}-${n}`;
34588
- }
34589
- usedDirs.add(dir);
34590
- const sorted = [...metas].sort((a, b2) => a.path.localeCompare(b2.path));
34591
- let latest = "1970-01-01T00:00:00.000Z";
34592
- const hasher = (0, import_node_crypto7.createHash)("sha256");
34593
- for (const m2 of sorted) {
34594
- if (m2.updatedAt > latest) latest = m2.updatedAt;
34595
- hasher.update(m2.path);
34596
- hasher.update("\0");
34597
- hasher.update(m2.blobHash);
34598
- hasher.update("\0");
34599
- }
34600
- out.push({
34601
- id: skill.id,
34602
- name: skill.name,
34603
- dir,
34604
- updatedAt: latest,
34605
- contentHash: `sha256:${hasher.digest("hex")}`,
34606
- fileList: sorted.map((m2) => m2.path)
34607
- });
34608
- }
34609
- return out;
34610
- }
34611
- function skillsDirForRuntime(runtimeKind) {
34612
- switch (runtimeKind) {
34613
- case "claude":
34614
- case "claude-code":
34615
- return ".claude/skills";
34616
- default:
34617
- return ".agent_context/skills";
34618
- }
34619
- }
34620
- function sanitizeSkillDir(name) {
34621
- const s2 = name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
34622
- return s2 || "skill";
34623
- }
34624
- async function materializeSkillFiles(args) {
34625
- const skills = await args.service.listInstalledSkillsForActor(args.actorId);
34626
- if (skills.length === 0) return {};
34627
- const prefix = skillsDirForRuntime(args.runtimeKind);
34628
- const out = {};
34629
- const usedDirs = /* @__PURE__ */ new Set();
34630
- for (const skill of skills) {
34631
- const files = await args.service.listSkillFiles(skill.id);
34632
- if (files.length === 0) continue;
34633
- let dir = sanitizeSkillDir(skill.name);
34634
- if (usedDirs.has(dir)) {
34635
- let n = 2;
34636
- while (usedDirs.has(`${dir}-${n}`)) n++;
34637
- dir = `${dir}-${n}`;
34638
- }
34639
- usedDirs.add(dir);
34640
- for (const f2 of files) {
34641
- if (!f2.content) {
34642
- console.warn(`[skill-materializer] skip ${skill.id}/${f2.path}\uFF1Acontent \u4E3A\u7A7A\uFF08\u672A\u56DE\u586B\uFF1F\uFF09\uFF0C\u4E0D\u5199\u7A7A\u6280\u80FD\u6587\u4EF6`);
34643
- continue;
34644
- }
34645
- out[`${prefix}/${dir}/${f2.path}`] = f2.content;
34646
- }
34647
- }
34648
- return out;
34649
- }
34650
- var import_node_crypto7;
34651
- var init_skill_materializer = __esm({
34652
- "../server/src/domains/actors/skill-materializer.ts"() {
34653
- "use strict";
34654
- import_node_crypto7 = require("node:crypto");
34690
+ init_skill_materializer();
34655
34691
  }
34656
34692
  });
34657
34693
 
@@ -36739,7 +36775,13 @@ var init_claude_code = __esm({
36739
36775
  fs7.mkdirSync(dir, { recursive: true });
36740
36776
  if (this.opts.syncSkills) {
36741
36777
  try {
36742
- await this.opts.syncSkills({ actorId: job.actor, runtimeKind: "claude", sessionDir: dir });
36778
+ await this.opts.syncSkills({
36779
+ actorId: job.actor,
36780
+ actorToken: job.actorToken,
36781
+ serverUrl: job.server.url,
36782
+ runtimeKind: "claude",
36783
+ sessionDir: dir
36784
+ });
36743
36785
  } catch (err) {
36744
36786
  console.warn(`[claude-code] syncSkills \u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`);
36745
36787
  }
@@ -37300,6 +37342,49 @@ var init_skill_cache = __esm({
37300
37342
  }
37301
37343
  });
37302
37344
 
37345
+ // ../adapters/src/_core/skill-cache-http.ts
37346
+ function makeHttpSkillMetadataSource(opts) {
37347
+ const doFetch = opts.fetchImpl ?? fetch;
37348
+ const timeout = opts.timeoutMs ?? 1e4;
37349
+ const base = opts.serverUrl.replace(/\/+$/, "");
37350
+ const headers = {
37351
+ "authorization": `Bearer ${opts.actorToken}`,
37352
+ "accept": "application/json"
37353
+ };
37354
+ const withDeadline = async (fn) => {
37355
+ const ctrl = new AbortController();
37356
+ const timer = setTimeout(() => ctrl.abort(), timeout);
37357
+ try {
37358
+ return await fn(ctrl.signal);
37359
+ } finally {
37360
+ clearTimeout(timer);
37361
+ }
37362
+ };
37363
+ return {
37364
+ listSkillMetadata: async (_actorId) => {
37365
+ return withDeadline(async (signal) => {
37366
+ const res = await doFetch(`${base}/api/skill-cache/manifest`, { headers, signal });
37367
+ if (!res.ok) throw new Error(`skill-cache manifest \u62C9\u53D6\u5931\u8D25\uFF1AHTTP ${res.status} ${res.statusText}`);
37368
+ const body = await res.json();
37369
+ return body.items ?? [];
37370
+ });
37371
+ },
37372
+ fetchSkillFiles: async (skillId) => {
37373
+ return withDeadline(async (signal) => {
37374
+ const res = await doFetch(`${base}/api/skill-cache/files/${encodeURIComponent(skillId)}`, { headers, signal });
37375
+ if (!res.ok) throw new Error(`skill-cache files \u62C9\u53D6\u5931\u8D25\uFF08${skillId}\uFF09\uFF1AHTTP ${res.status} ${res.statusText}`);
37376
+ const body = await res.json();
37377
+ return body.files ?? [];
37378
+ });
37379
+ }
37380
+ };
37381
+ }
37382
+ var init_skill_cache_http = __esm({
37383
+ "../adapters/src/_core/skill-cache-http.ts"() {
37384
+ "use strict";
37385
+ }
37386
+ });
37387
+
37303
37388
  // ../adapters/src/_core/models.ts
37304
37389
  async function listModels(runtime, executablePath) {
37305
37390
  switch (runtime) {
@@ -37556,6 +37641,19 @@ var init_models = __esm({
37556
37641
  function codexNum(v2) {
37557
37642
  return typeof v2 === "number" && Number.isFinite(v2) ? v2 : 0;
37558
37643
  }
37644
+ function parseCodexSessionId(lines) {
37645
+ const first = lines[0];
37646
+ if (!first) return void 0;
37647
+ let o;
37648
+ try {
37649
+ o = JSON.parse(first);
37650
+ } catch {
37651
+ return void 0;
37652
+ }
37653
+ if (o?.type !== "session_meta") return void 0;
37654
+ const id = o.payload?.id;
37655
+ return typeof id === "string" && id ? id : void 0;
37656
+ }
37559
37657
  function parseCodexTokenUsage(lines) {
37560
37658
  let last;
37561
37659
  for (const line of lines) {
@@ -37660,7 +37758,12 @@ function scanCodexTelemetry(workdir, sinceMs, sessionsRoot = codexSessionsRoot()
37660
37758
  if (meta?.type === "session_meta" && meta.payload?.cwd === workdir) {
37661
37759
  const usage = parseCodexTokenUsage(lines);
37662
37760
  const model = parseCodexModel(lines);
37663
- return { ...usage ? { usage } : {}, ...model ? { model } : {} };
37761
+ const sessionId = parseCodexSessionId(lines);
37762
+ return {
37763
+ ...usage ? { usage } : {},
37764
+ ...model ? { model } : {},
37765
+ ...sessionId ? { sessionId } : {}
37766
+ };
37664
37767
  }
37665
37768
  }
37666
37769
  } catch {
@@ -37861,8 +37964,9 @@ var init_codex = __esm({
37861
37964
  const task = job.bundle.files["TASK.md"];
37862
37965
  if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
37863
37966
  const prompt = job.systemPrompt ? ["# System Prompt", job.systemPrompt, "", "# Task", task].join("\n") : task;
37967
+ const resumeArgs = job.resumeRuntimeSession && job.runtimeSessionId ? ["exec", "resume", job.runtimeSessionId] : ["exec"];
37864
37968
  const args = [
37865
- "exec",
37969
+ ...resumeArgs,
37866
37970
  "--json",
37867
37971
  "--dangerously-bypass-approvals-and-sandbox",
37868
37972
  "--skip-git-repo-check",
@@ -37949,12 +38053,14 @@ var init_codex = __esm({
37949
38053
  clearTimeout(timer);
37950
38054
  const telemetry = scanCodexTelemetry(dir, startedAtMs);
37951
38055
  const model = telemetry.model ?? job.model ?? this.opts.model;
38056
+ const runtimeSessionId = telemetry.sessionId ?? (job.resumeRuntimeSession ? job.runtimeSessionId : void 0);
37952
38057
  finish({
37953
38058
  code: killedByUs ? null : code,
37954
38059
  reason: killedByUs ? "timeout" : code === 0 ? "clean" : "error",
37955
38060
  transcriptRef,
37956
38061
  ...telemetry.usage ? { usage: telemetry.usage } : {},
37957
- ...model ? { model } : {}
38062
+ ...model ? { model } : {},
38063
+ ...runtimeSessionId ? { runtimeSessionId } : {}
37958
38064
  });
37959
38065
  });
37960
38066
  return {
@@ -38406,9 +38512,11 @@ async function runACPSession(job, cfg) {
38406
38512
  let exited;
38407
38513
  let killedByUs = false;
38408
38514
  let actualModel;
38515
+ let capturedSessionId;
38409
38516
  const finish = (info) => {
38410
38517
  if (exited) return;
38411
38518
  if (actualModel && !info.model) info = { ...info, model: actualModel };
38519
+ if (capturedSessionId && !info.runtimeSessionId) info = { ...info, runtimeSessionId: capturedSessionId };
38412
38520
  if (cfg.cleanupWorkdir) {
38413
38521
  try {
38414
38522
  const keepDir = path9.join(cfg.workRoot ?? os5.tmpdir(), "oasis-transcripts");
@@ -38481,13 +38589,31 @@ async function runACPSession(job, cfg) {
38481
38589
  clientCapabilities: {}
38482
38590
  });
38483
38591
  const resolvedModel = job.model ?? cfg.model;
38484
- const sessionRes = await client.request("session/new", {
38485
- cwd: dir,
38486
- mcpServers: [],
38487
- ...resolvedModel ? { model: resolvedModel } : {}
38488
- });
38489
- const sessionId = extractSessionID(sessionRes);
38490
- if (!sessionId) throw new Error("session/new returned no sessionId");
38592
+ let sessionId = "";
38593
+ if (job.resumeRuntimeSession && job.runtimeSessionId) {
38594
+ const method = cfg.resumeMethod ?? "session/load";
38595
+ try {
38596
+ const resumeRes = await client.request(method, {
38597
+ cwd: dir,
38598
+ sessionId: job.runtimeSessionId,
38599
+ mcpServers: [],
38600
+ ...resolvedModel ? { model: resolvedModel } : {}
38601
+ });
38602
+ sessionId = extractSessionID(resumeRes) || job.runtimeSessionId;
38603
+ } catch {
38604
+ sessionId = "";
38605
+ }
38606
+ }
38607
+ if (!sessionId) {
38608
+ const sessionRes = await client.request("session/new", {
38609
+ cwd: dir,
38610
+ mcpServers: [],
38611
+ ...resolvedModel ? { model: resolvedModel } : {}
38612
+ });
38613
+ sessionId = extractSessionID(sessionRes);
38614
+ if (!sessionId) throw new Error("session/new returned no sessionId");
38615
+ }
38616
+ capturedSessionId = sessionId;
38491
38617
  if (resolvedModel) {
38492
38618
  await client.request("session/set_model", { sessionId, modelId: resolvedModel });
38493
38619
  actualModel = resolvedModel;
@@ -39496,6 +39622,7 @@ var init_src4 = __esm({
39496
39622
  init_claude_code();
39497
39623
  init_resilient();
39498
39624
  init_skill_cache();
39625
+ init_skill_cache_http();
39499
39626
  init_models();
39500
39627
  init_codex();
39501
39628
  init_hermes();
@@ -54904,12 +55031,14 @@ async function startServe(opts) {
54904
55031
  for (const cb of telemetryCbs) cb(event);
54905
55032
  enqueueTrace(() => traceStore.appendEvents(traceRunId, [trajectoryEventToRunEvent(event, ++traceSeq)]).then(() => void 0));
54906
55033
  });
55034
+ let capturedNativeSessionId;
54907
55035
  const done = new Promise((resolve4, reject) => {
54908
55036
  handle.onExit((info) => {
54909
55037
  chatLiveSessions.delete(chatJobKey);
54910
55038
  void provision.cleanup();
54911
55039
  const endedAt = (/* @__PURE__ */ new Date()).toISOString();
54912
55040
  const durationMs = Date.parse(endedAt) - Date.parse(traceStartedAt);
55041
+ if (info.runtimeSessionId) capturedNativeSessionId = info.runtimeSessionId;
54913
55042
  enqueueTrace(async () => {
54914
55043
  await traceStore.appendEvents(traceRunId, [{
54915
55044
  seq: ++traceSeq,
@@ -54954,7 +55083,10 @@ async function startServe(opts) {
54954
55083
  telemetryCbs.push(cb);
54955
55084
  },
54956
55085
  done,
54957
- kill: () => handle.kill()
55086
+ kill: () => handle.kill(),
55087
+ get nativeSessionId() {
55088
+ return capturedNativeSessionId;
55089
+ }
54958
55090
  };
54959
55091
  },
54960
55092
  trajectoryFeed: {
@@ -55868,7 +56000,27 @@ async function startNode(opts) {
55868
56000
  } else {
55869
56001
  console.log(`[oasis node] \u5DF2\u627E\u5230: ${adapters.join(", ")}`);
55870
56002
  }
55871
- const claude = new ClaudeCodeAdapter({ streamJson: true });
56003
+ const skillCacheRoot = process.env["OASIS_SKILL_CACHE_ROOT"]?.trim();
56004
+ const skillCacheEnabled = process.env["OASIS_SKILL_CACHE"] === "1" || !!skillCacheRoot;
56005
+ if (skillCacheEnabled) {
56006
+ console.log(`[oasis node] skill-cache \u5F00\u542F\uFF0Croot=${skillCacheRoot ?? "~/.oasis/skill-cache"}`);
56007
+ }
56008
+ const claudeSyncSkills = skillCacheEnabled ? async (args) => {
56009
+ const source = makeHttpSkillMetadataSource({ serverUrl: args.serverUrl, actorToken: args.actorToken });
56010
+ await syncActorSkills({
56011
+ actorId: args.actorId,
56012
+ runtimeKind: args.runtimeKind,
56013
+ sessionDir: args.sessionDir,
56014
+ source,
56015
+ ...skillCacheRoot ? { cacheRoot: skillCacheRoot } : {},
56016
+ logger: (level, msg, err) => {
56017
+ const prefix = `[skill-cache:node] ${msg}`;
56018
+ if (level === "warn") console.warn(prefix, err ?? "");
56019
+ else console.log(prefix);
56020
+ }
56021
+ });
56022
+ } : void 0;
56023
+ const claude = new ClaudeCodeAdapter({ streamJson: true, ...claudeSyncSkills ? { syncSkills: claudeSyncSkills } : {} });
55872
56024
  const rawAdapters = {
55873
56025
  "claude-code": claude,
55874
56026
  claude,
@@ -56009,15 +56161,16 @@ var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uF
56009
56161
  spawn <artifactId> --type <t> [--title <\u77ED\u540D>] [--brief <\u89C4\u683C>] [--input <\u4E0A\u6E38id>[,<id>\u2026]] [--owner <actorId>] [--workspace ws] [--persistence temp|persistent] [--parts-file <json>] [--<\u5B57\u6BB5> <\u503C>] [--set-json <json>]
56010
56162
  # --parts-file\uFF1Aconvergent \u5E76\u884C\u5206\u89E3\u7684\u90E8\u4EF6\u6E05\u5355 JSON [{name, spec?, deps?}]\uFF08Stage 3\uFF0C\u7CFB\u7EDF\u6309\u90E8\u4EF6\u6D3E N \u8DEF\uFF09
56011
56163
  # --<\u5B57\u6BB5> <\u503C>\uFF1A\u8BE5\u7C7B\u578B\u3010\u52A8\u6001\u58F0\u660E\u3011\u7684\u81EA\u5B9A\u4E49\u5B57\u6BB5\uFF08\u4E0D\u662F\u56FA\u5B9A\u6E05\u5355\uFF09\u2014\u2014\u7528 "oasis artifact-types show <type>" \u770B\u53EF\u586B\u5B57\u6BB5\uFF1Bstring[]/\u6279\u91CF\u7528 --set-json\uFF1B\u7F3A\u5FC5\u586B\u4F1A\u62A5\u9519\u5217\u51FA
56012
- propose <artifactId> (--file <path> | --from-dir <path> | --git-commit <sha>) --reason <text> [--base <revisionId|none>] [--part <name>] [--rebase-of <revisionId>] [--checkpoint --continue]
56164
+ propose <artifactId> [--file <path> | --from-dir <path> | --git-commit <sha>] --reason <text> [--base <revisionId|none>] [--part <name>] [--rebase-of <revisionId>] [--checkpoint --continue]
56013
56165
  # --file \u5355\u6587\u4EF6\uFF1B--from-dir \u9012\u5F52\u6574\u76EE\u5F55\u4E3A\u591A\u6587\u4EF6 revision\uFF08\u88C5\u7BB1\u5355\uFF0C\u8DE8\u7248\u672C\u53BB\u91CD\uFF0CF1\uFF09\u3002
56014
- # --git-commit\uFF1AModel Y \u4EE3\u7801-git project\u2014\u2014\u5185\u5BB9\u6D3B\u5728 git\uFF0C\u53EA external-pin \u90A3\u4E2A commit sha\uFF08\u5148 push \u4F60\u7684 feat \u5206\u652F\uFF09\u3002
56166
+ # \u4EE3\u7801\u8282\u70B9\u53EF\u3010\u7701\u7565\u5168\u90E8\u5185\u5BB9\u53C2\u6570\u3011\uFF0C\u4EC5 --reason\uFF1A\u5148\u628A\u6539\u52A8 push \u5230\u5DE5\u5355 feat \u5206\u652F\uFF0C\u7CFB\u7EDF\u81EA\u52A8\u4ECE git \u6536\u96C6\uFF08\u672C\u4F53\u5728 git\uFF09\u3002
56167
+ # --git-commit\uFF1A\u624B\u52A8 external-pin \u90A3\u4E2A commit sha\uFF08\u7B49\u4EF7\u4E8E\u7CFB\u7EDF\u6536\u96C6\uFF0C\u7528\u4E8E\u7CBE\u786E\u6307\u5B9A\uFF09\u3002
56015
56168
  # --part\uFF1A\u672C revision \u4EA7 convergent \u5206\u89E3\u91CC\u7684\u54EA\u4E2A\u90E8\u4EF6\uFF08\u89C1 spawn --parts-file\uFF09
56016
56169
  # \u6CE8\u610F --from-dir \u662F"\u5185\u5BB9\u6E90\u76EE\u5F55"\uFF0C\u4E0E\u5168\u5C40 --dir\uFF08\u6570\u636E\u76EE\u5F55/operator-token \u6240\u5728\uFF09\u662F\u4E24\u56DE\u4E8B
56017
56170
  queue <artifactId>
56018
56171
  merge <artifactId> [revisionId]
56019
56172
  advance <artifactId>
56020
- integrate <artifactId> (--file <path> | --from-dir <path>) --reason <text>
56173
+ integrate <artifactId> [--file <path> | --from-dir <path> | --git-commit <sha>] --reason <text> # \u4EE3\u7801\u8282\u70B9\u53EF\u7701\u7565\u5185\u5BB9\u53C2\u6570\uFF1A\u7CFB\u7EDF\u4ECE\u5DE5\u5355 feat \u5206\u652F\u6536\u96C6\u6574\u5408\u7248
56021
56174
  reject <artifactId> <revisionId> --reason <text>
56022
56175
  conclude <artifactId> [--note <text>] # \u673A\u68B0 grounding \u786C\u62E6\uFF1B\u6709 gate \u5219\u8FDB\u5165\u5F85\u5BA1
56023
56176
  review <artifactId> --verdict approve|request_changes [--note <t>]
@@ -56891,12 +57044,15 @@ ${res.warning}`);
56891
57044
  const baseFlag = flags.get("base");
56892
57045
  const dirFlag = flags.get("from-dir");
56893
57046
  const gitCommit = flags.get("git-commit");
57047
+ const fileFlag = flags.get("file");
56894
57048
  const checkpoint = flags.get("checkpoint") === "true";
56895
57049
  const continueDispatch = flags.get("continue") === "true";
56896
57050
  if (checkpoint !== continueDispatch) throw new Error("--checkpoint \u4E0E --continue \u5FC5\u987B\u540C\u65F6\u4F7F\u7528");
56897
57051
  const { message } = await api.cmd("propose", {
56898
57052
  artifactId,
56899
- ...gitCommit !== void 0 ? { externalPin: gitCommit } : dirFlag !== void 0 ? { files: readDirFiles(dirFlag) } : { content: readFileArg(need(flags, "file")) },
57053
+ // 内容四选一——【全缺则不带内容字段】,交服务端按节点类型定夺(code:从 git 收集;text:精确报错)。
57054
+ // 这是 HANDOFF 止血:从前 CLI 本地就 need(--file) 抛错,逼 agent 兜底成 --file HANDOFF.md 落 inline 假货。
57055
+ ...gitCommit !== void 0 ? { externalPin: gitCommit } : dirFlag !== void 0 ? { files: readDirFiles(dirFlag) } : fileFlag !== void 0 ? { content: readFileArg(fileFlag) } : {},
56900
57056
  reason: need(flags, "reason"),
56901
57057
  ...baseFlag !== void 0 ? { base: baseFlag === "none" ? null : baseFlag } : {},
56902
57058
  ...flags.get("part") !== void 0 ? { part: flags.get("part") } : {},
@@ -56924,11 +57080,14 @@ ${res.warning}`);
56924
57080
  break;
56925
57081
  }
56926
57082
  case "integrate": {
56927
- const artifactId = needPos(positional, 0, "oasis integrate <artifactId> (--file f | --from-dir d) --reason r");
57083
+ const artifactId = needPos(positional, 0, "oasis integrate <artifactId> [--file f | --from-dir d | --git-commit sha] --reason r");
56928
57084
  const dirFlag = flags.get("from-dir");
57085
+ const fileFlag = flags.get("file");
57086
+ const gitCommit = flags.get("git-commit");
56929
57087
  const { message } = await api.cmd("integrate", {
56930
57088
  artifactId,
56931
- ...dirFlag !== void 0 ? { files: readDirFiles(dirFlag) } : { content: readFileArg(need(flags, "file")) },
57089
+ // 内容全缺则不带内容字段,交服务端定夺(code:从工单 feat 分支收集整合版;text:精确报错)。HANDOFF 止血。
57090
+ ...gitCommit !== void 0 ? { externalPin: gitCommit } : dirFlag !== void 0 ? { files: readDirFiles(dirFlag) } : fileFlag !== void 0 ? { content: readFileArg(fileFlag) } : {},
56932
57091
  reason: need(flags, "reason")
56933
57092
  });
56934
57093
  println(message);
@@ -56961,11 +57120,12 @@ ${res.warning}`);
56961
57120
  break;
56962
57121
  }
56963
57122
  case "annotate": {
57123
+ const raisedFromPart = flags.get("part") ?? process.env["OASIS_PART"];
56964
57124
  const { message } = await api.cmd("annotate", {
56965
57125
  artifactId: needPos(positional, 0, "oasis annotate <artifactId> --body t [--from d] [--part p]"),
56966
57126
  body: need(flags, "body"),
56967
57127
  raisedFrom: flags.get("from"),
56968
- ...flags.get("part") !== void 0 ? { raisedFromPart: flags.get("part") } : {},
57128
+ ...raisedFromPart !== void 0 ? { raisedFromPart } : {},
56969
57129
  // @(信箱 ④):--at a,b —— @ 权只属于人(agent 用会被服务端拒);@agent=召它回话,@人=进其收件箱
56970
57130
  ...flags.get("at") !== void 0 ? { mentions: flags.get("at").split(",").map((x2) => x2.trim()).filter(Boolean) } : {},
56971
57131
  blocks: flags.get("blocks") === "true"
@@ -57472,7 +57632,7 @@ ${res.warning}`);
57472
57632
  }
57473
57633
 
57474
57634
  // src/index.ts
57475
- var PKG_VERSION = true ? "0.1.32" : "dev";
57635
+ var PKG_VERSION = true ? "0.1.34" : "dev";
57476
57636
  var OASIS_DIR = path18.join(os10.homedir(), ".oasis");
57477
57637
  var CONFIG_FILE = path18.join(OASIS_DIR, "node-config.json");
57478
57638
  var PID_FILE = path18.join(OASIS_DIR, "node.pid");
@@ -57613,7 +57773,7 @@ function selfUpdate() {
57613
57773
  if (!cfg?.serverUrl) throw new Error("no config \u2014 run `oasis start` first");
57614
57774
  const nameArg = cfg.name ? ` --name '${cfg.name.replace(/'/g, "'\\''")}'` : "";
57615
57775
  const startCmd = `"$(npm prefix -g)/bin/oasis" start --server-ws '${cfg.serverUrl}'${nameArg}`;
57616
- const inner = `sleep 2; npm install -g --prefer-online '${PKG_NAME}@latest' && ${startCmd} >> '${LOG_FILE}' 2>&1`;
57776
+ const inner = `{ echo "[oasis] self-update \u2192 npm install -g ${PKG_NAME}@latest (then restart)"; sleep 2; npm install -g --prefer-online '${PKG_NAME}@latest' && ${startCmd}; } >> '${LOG_FILE}' 2>&1`;
57617
57777
  const hasSystemd = process.platform === "linux" && (() => {
57618
57778
  try {
57619
57779
  (0, import_node_child_process15.execSync)("command -v systemd-run", { stdio: "ignore" });
@@ -57622,14 +57782,14 @@ function selfUpdate() {
57622
57782
  return false;
57623
57783
  }
57624
57784
  })();
57785
+ const log3 = fs19.openSync(LOG_FILE, "a");
57625
57786
  if (hasSystemd) {
57626
57787
  const child = (0, import_node_child_process15.spawn)("systemd-run", ["--user", "--scope", "--quiet", "sh", "-c", inner], {
57627
57788
  detached: true,
57628
- stdio: "ignore"
57789
+ stdio: ["ignore", log3, log3]
57629
57790
  });
57630
57791
  child.unref();
57631
57792
  } else {
57632
- const log3 = fs19.openSync(LOG_FILE, "a");
57633
57793
  const child = (0, import_node_child_process15.spawn)("sh", ["-c", inner], { detached: true, stdio: ["ignore", log3, log3] });
57634
57794
  child.unref();
57635
57795
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test",
3
- "version": "0.1.32",
3
+ "version": "0.1.34",
4
4
  "description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
5
5
  "bin": {
6
6
  "oasis": "./dist/index.js"