oasis_test 0.1.93 → 0.1.94

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 +384 -148
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -2409,6 +2409,15 @@ var init_workorder_attachments = __esm({
2409
2409
  }
2410
2410
  });
2411
2411
 
2412
+ // ../contract/src/chat-attachments.ts
2413
+ var CHAT_ATTACHMENT_MAX_BYTES;
2414
+ var init_chat_attachments = __esm({
2415
+ "../contract/src/chat-attachments.ts"() {
2416
+ "use strict";
2417
+ CHAT_ATTACHMENT_MAX_BYTES = 50 * 1024 * 1024;
2418
+ }
2419
+ });
2420
+
2412
2421
  // ../contract/src/schema.ts
2413
2422
  var init_schema = __esm({
2414
2423
  "../contract/src/schema.ts"() {
@@ -2515,9 +2524,11 @@ var init_daemon_protocol = __esm({
2515
2524
  });
2516
2525
 
2517
2526
  // ../contract/src/registry.ts
2527
+ var AGENT_PREFS_DEFAULTS;
2518
2528
  var init_registry = __esm({
2519
2529
  "../contract/src/registry.ts"() {
2520
2530
  "use strict";
2531
+ AGENT_PREFS_DEFAULTS = { memoryEnabled: true };
2521
2532
  }
2522
2533
  });
2523
2534
 
@@ -2661,6 +2672,7 @@ var init_src = __esm({
2661
2672
  init_inbox_read();
2662
2673
  init_workorder_quality();
2663
2674
  init_workorder_attachments();
2675
+ init_chat_attachments();
2664
2676
  init_schema();
2665
2677
  init_errors();
2666
2678
  init_storage();
@@ -4501,12 +4513,16 @@ ${res.candidates.map((c) => ` - ${c.type}:${c.title ?? "(\u65E0title)"} \u2192
4501
4513
  type: op.type,
4502
4514
  owner,
4503
4515
  workspace: op.workspace ?? "ws:default",
4504
- persistence: "persistent",
4516
+ // 改图/草案 spawn 的默认持久性仍是 persistent(图节点落库即持久);显式带 persistence
4517
+ // (如暂存态 `spawn --persistence temp`)时按它落——否则 CLI 发上来的值会在这里被丢掉。
4518
+ persistence: op.persistence ?? "persistent",
4505
4519
  createdVia: isPrereqFill(spawnId) ? "prereq" : "manual-spawn",
4506
4520
  ...op.isFinalOutput !== void 0 ? { isFinalOutput: op.isFinalOutput } : {},
4507
4521
  ...op.title !== void 0 ? { title: op.title } : {},
4508
4522
  ...op.description !== void 0 ? { description: op.description } : {},
4509
4523
  ...op.docType !== void 0 ? { docType: op.docType } : {},
4524
+ // 并行分解部件清单:随 spawn 原子落地(同 kernel.spawnArtifact 的 parts 通路)。
4525
+ ...op.parts !== void 0 ? { parts: op.parts } : {},
4510
4526
  ...spawnFields !== void 0 ? { fields: spawnFields } : {},
4511
4527
  ...op.reviewers !== void 0 ? { reviewers: op.reviewers } : {},
4512
4528
  ...op.reviewRoles !== void 0 ? { reviewRoles: op.reviewRoles } : {},
@@ -5229,12 +5245,14 @@ async function assembleContext(args) {
5229
5245
  const a = args.annotationId !== void 0 ? model.annotations.get(args.annotationId) : void 0;
5230
5246
  if (!a) throw new Error(`assembleContext(reply): annotation not found: ${args.annotationId}`);
5231
5247
  const newPostLines = renderLetterDetail(model, a, args.actorId, actorLabel);
5248
+ let replyMemoryEnabled = true;
5232
5249
  if (args.resolveActorContext && args.actorId) {
5233
5250
  const actorCtx = await args.resolveActorContext(args.actorId).catch(() => null);
5251
+ replyMemoryEnabled = actorCtx?.memoryEnabled !== false;
5234
5252
  if (actorCtx?.config?.prompt) Object.assign(files, splitIdentityFiles(actorCtx.config.prompt));
5235
5253
  }
5236
5254
  if (args.materializeSkills) Object.assign(files, await args.materializeSkills().catch(() => ({})));
5237
- const replyMemories = args.resolveActorMemoryIndex ? await args.resolveActorMemoryIndex(memoryQueryTerms([a.body, artifact.title, artifact.description])).catch(() => []) : [];
5255
+ const replyMemories = args.resolveActorMemoryIndex && replyMemoryEnabled ? await args.resolveActorMemoryIndex(memoryQueryTerms([a.body, artifact.title, artifact.description])).catch(() => []) : [];
5238
5256
  files["TASK.md"] = [
5239
5257
  `# \u56DE\u590D\u8F6E\uFF1A\u4F60\u88AB @ \u8FDB\u4E00\u5C01\u4FE1\u91CC\u56DE\u8BDD`,
5240
5258
  ``,
@@ -5592,24 +5610,17 @@ ${manifestDiff(baseC, newC)}
5592
5610
  );
5593
5611
  }
5594
5612
  }
5595
- const memoryIndex = args.resolveActorMemoryIndex ? await args.resolveActorMemoryIndex(
5596
- memoryQueryTerms([
5597
- partSpec?.spec,
5598
- artifact.title,
5599
- ...latestRejections.map((v2) => v2.note),
5600
- ...openAnnosHere.filter((a) => a.kind === "change-request").map((a) => a.body),
5601
- artifact.description
5602
- ])
5603
- ).catch(() => []) : [];
5604
5613
  let skillsLines = [];
5605
5614
  let identityNote = null;
5606
5615
  let batchedContinuation = false;
5616
+ let memoryEnabled = true;
5607
5617
  let executorRole = null;
5608
5618
  const executionActor = args.actorId ?? artifact.owner;
5609
5619
  if (args.resolveActorContext) {
5610
5620
  const actorCtx = await args.resolveActorContext(executionActor).catch(() => null);
5611
5621
  if (actorCtx) {
5612
5622
  batchedContinuation = actorCtx.config?.batchedContinuation === true;
5623
+ memoryEnabled = actorCtx.memoryEnabled !== false;
5613
5624
  executorRole = actorCtx.roles?.[0] ?? null;
5614
5625
  if (actorCtx.config?.prompt) {
5615
5626
  const identityFiles = splitIdentityFiles(actorCtx.config.prompt);
@@ -5635,6 +5646,16 @@ ${manifestDiff(baseC, newC)}
5635
5646
  }
5636
5647
  }
5637
5648
  }
5649
+ const memoryIndex = args.resolveActorMemoryIndex && memoryEnabled ? await args.resolveActorMemoryIndex(
5650
+ memoryQueryTerms([
5651
+ partSpec?.spec,
5652
+ artifact.title,
5653
+ ...latestRejections.map((v2) => v2.note),
5654
+ ...openAnnosHere.filter((a) => a.kind === "change-request").map((a) => a.body),
5655
+ artifact.description
5656
+ ])
5657
+ ).catch(() => []) : [];
5658
+ const memoryLines = !canvasMode && memoryEnabled;
5638
5659
  if (args.materializeSkills) {
5639
5660
  const skillFiles = await args.materializeSkills().catch(() => ({}));
5640
5661
  for (const [p2, c] of Object.entries(skillFiles)) files[p2] = c;
@@ -5865,14 +5886,16 @@ ${m2.spec.split("\n").map((l) => ` > ${l}`).join("\n")}`);
5865
5886
  ...artifact.workspace ? { workspace: artifact.workspace } : {}
5866
5887
  }),
5867
5888
  // ADR-0084 / 0105:记忆索引(数据)+ 共享话术常量(命令 + 引导 + 收工前反思自监控)。
5868
- // canvasMode 例外——画板会话的内层 agent 没有 oasis CLI(拿不到 per-dispatch token),
5869
- // 给它记忆命令是空头支票。
5870
- ...canvasMode ? [] : memoryIndexLines(memoryIndex),
5871
- ...canvasMode ? [] : memoryHowToLines(),
5889
+ // 两种情况都整段不给(判据合成 memoryLines):
5890
+ // - canvasMode——画板会话的内层 agent 没有 oasis CLI(拿不到 per-dispatch token),给它记忆命令是空头支票;
5891
+ // - memoryEnabled=false——该员工的长期记忆总开关关了,连带下面的反思升级块一起去掉(那段话术
5892
+ // 通篇指着 mem-write,留着就是让 agent 去撞 403)。
5893
+ ...memoryLines ? memoryIndexLines(memoryIndex) : [],
5894
+ ...memoryLines ? memoryHowToLines() : [],
5872
5895
  // 反思升级块(ADR-0105):只在系统能检测到的高价值时刻推、只推 agent 未必知道的事实。
5873
5896
  // 返工三源:上游变更失据 / 评审打回 / 人工变更请求。失败源:本会话失败 > 3 次(事不过三)。
5874
5897
  // 情感 / "长时间尝试终于成功" 检测不到,交给上面自监控清单,不在这里。
5875
- ...canvasMode ? [] : memoryReflectionEscalation({
5898
+ ...!memoryLines ? [] : memoryReflectionEscalation({
5876
5899
  reworkReasons: reworkReasonsOf({
5877
5900
  unfounded: unfoundedHere.length > 0,
5878
5901
  rejected: latestRejections.length > 0,
@@ -138528,6 +138551,13 @@ function editDraftPlan(draft, op) {
138528
138551
  type: op.type,
138529
138552
  workspace: draft.workspace,
138530
138553
  title,
138554
+ // owner/persistence/parts 与 CLI `spawn` 声明的 own flag 对齐——早先这里只 push 了
138555
+ // id/type/title/description/fields/inputs,`--owner`/`--persistence`/`--parts-file` 即便被 CLI 发到
138556
+ // /api/stage 也在这一步被静默丢掉(诊断 4abeac9e §①A/§④A-服务端草案)。assign 分支能改 spawn op 的
138557
+ // owner,说明「草案里 owner 有意义」早已成立;persistence/parts 随 InterventionOp spawn 变体一并放开。
138558
+ ...op.owner !== void 0 ? { owner: op.owner } : {},
138559
+ ...op.persistence !== void 0 ? { persistence: op.persistence } : {},
138560
+ ...op.parts !== void 0 ? { parts: op.parts } : {},
138531
138561
  ...op.description !== void 0 ? { description: op.description } : {},
138532
138562
  ...op.fields ? { fields: op.fields } : {},
138533
138563
  ...inputs && inputs.length > 0 ? { inputs } : {}
@@ -140081,6 +140111,30 @@ function planningStatusOf(_model, arts, resolveRoleExecutor) {
140081
140111
  }
140082
140112
  return issues.length > 0 ? { status: "needs_staffing", issues } : { status: "ready", issues: [] };
140083
140113
  }
140114
+ function draftPlanningStatusOf(spawns, isRoleStaffed) {
140115
+ const issues = [];
140116
+ const seenRoles = /* @__PURE__ */ new Set();
140117
+ for (const s2 of spawns) {
140118
+ const owner = s2.owner;
140119
+ if (owner === void 0 || owner === PENDING_UNASSIGNED) {
140120
+ issues.push({
140121
+ code: "missing_owner",
140122
+ severity: "warning",
140123
+ artifactType: s2.type,
140124
+ message: `${s2.id} \u5C1A\u672A\u6307\u5B9A\u8D1F\u8D23\u4EBA\u2014\u2014\u5EFA\u5355\u524D\u8BF7\u5728\u8349\u6848\u91CC\u6307\u5B9A\uFF0C\u5426\u5219\u843D\u6210\u5F85\u6D3E\u3002`
140125
+ });
140126
+ continue;
140127
+ }
140128
+ if (owner.startsWith(PENDING_ROLE_PREFIX)) {
140129
+ const role = owner.slice(PENDING_ROLE_PREFIX.length);
140130
+ if (isRoleStaffed(role)) continue;
140131
+ if (seenRoles.has(role)) continue;
140132
+ seenRoles.add(role);
140133
+ issues.push({ code: "missing_role", severity: "warning", role, message: `${role} \u89D2\u8272\u65E0 active \u5458\u5DE5\uFF0C\u76F8\u5173\u8282\u70B9\u5F85\u6D3E\u2014\u2014\u8BF7\u8865\u4EBA\u6216 assign\u3002` });
140134
+ }
140135
+ }
140136
+ return issues.length > 0 ? { status: "needs_staffing", issues } : { status: "ready", issues: [] };
140137
+ }
140084
140138
  function specFromFields(fields) {
140085
140139
  if (!fields) return null;
140086
140140
  const out = {};
@@ -140399,7 +140453,7 @@ function buildTypeReviewerResolver(types2, actors) {
140399
140453
  };
140400
140454
  };
140401
140455
  }
140402
- function buildWorkorderDraftDetail(draft, resolveActor, typeReviewerPreview) {
140456
+ function buildWorkorderDraftDetail(draft, resolveActor, typeReviewerPreview, resolveRoleStaffed) {
140403
140457
  const ref2 = (id) => {
140404
140458
  const extra = resolveActor?.(id);
140405
140459
  return { id, ...extra?.name ? { name: extra.name } : {}, ...extra?.role ? { role: extra.role } : {}, ...extra?.avatar ? { avatar: extra.avatar } : {} };
@@ -140475,13 +140529,18 @@ function buildWorkorderDraftDetail(draft, resolveActor, typeReviewerPreview) {
140475
140529
  projectId: null,
140476
140530
  ...draft.seed.dispatch === false ? { dispatchPaused: true } : {}
140477
140531
  };
140532
+ const planning = draftPlanningStatusOf(
140533
+ spawns.filter((op) => op.type !== DELIVERY_SUMMARY_ARTIFACT_TYPE).map((op) => ({ id: op.id, type: op.type, ...op.owner !== void 0 ? { owner: op.owner } : {} })),
140534
+ resolveRoleStaffed ?? (() => true)
140535
+ );
140478
140536
  return {
140479
140537
  summary,
140480
140538
  graph: { nodes, edges },
140481
140539
  activity: [],
140482
140540
  coordinatorActivity: [],
140483
140541
  acceptance: [],
140484
- ...draft.seed.acceptanceCriteria ? { acceptanceCriteria: draft.seed.acceptanceCriteria } : {}
140542
+ ...draft.seed.acceptanceCriteria ? { acceptanceCriteria: draft.seed.acceptanceCriteria } : {},
140543
+ planning
140485
140544
  };
140486
140545
  }
140487
140546
  function isRuntimeFailure(runtime) {
@@ -147502,6 +147561,13 @@ async function draftActorResolver(registry2) {
147502
147561
  return a ? { name: a.name, ...a.roles[0] ? { role: a.roles[0] } : {}, ...a.avatar ? { avatar: a.avatar } : {} } : void 0;
147503
147562
  };
147504
147563
  }
147564
+ async function draftRoleStaffingResolver(registry2) {
147565
+ if (!registry2) return () => true;
147566
+ const actors = await registry2.listActors();
147567
+ const staffedRoles = /* @__PURE__ */ new Set();
147568
+ for (const a of actors) if (a.status === "active") for (const r of a.roles) staffedRoles.add(r);
147569
+ return (role) => staffedRoles.has(role);
147570
+ }
147505
147571
  async function draftTypeReviewerResolver(schema, registry2) {
147506
147572
  if (!schema || !registry2) return void 0;
147507
147573
  return buildTypeReviewerResolver(schema, await registry2.listActors());
@@ -149256,8 +149322,9 @@ async function startOasisServer(opts) {
149256
149322
  res.writeHead(404, { "content-type": "application/json" }).end(JSON.stringify({ error: "no staged workorder draft" }));
149257
149323
  return;
149258
149324
  }
149325
+ const draftDetail = buildWorkorderDraftDetail(draft, await draftActorResolver(engine.registry), await draftTypeReviewerResolver(opts.schema, engine.registry), await draftRoleStaffingResolver(engine.registry));
149259
149326
  res.writeHead(200, { "content-type": "application/json" }).end(
149260
- JSON.stringify({ data: { ...draft, graph: buildWorkorderDraftDetail(draft, await draftActorResolver(engine.registry), await draftTypeReviewerResolver(opts.schema, engine.registry)).graph } })
149327
+ JSON.stringify({ data: { ...draft, graph: draftDetail.graph, ...draftDetail.planning ? { planning: draftDetail.planning } : {} } })
149261
149328
  );
149262
149329
  return;
149263
149330
  }
@@ -149267,8 +149334,12 @@ async function startOasisServer(opts) {
149267
149334
  const chain = workspace ? await opts.workorderDrafts.historyForWorkspace(workspace) : session ? await opts.workorderDrafts.historyForSession(session) : [];
149268
149335
  const resolver = await draftActorResolver(engine.registry);
149269
149336
  const chainTypeRv = await draftTypeReviewerResolver(opts.schema, engine.registry);
149337
+ const chainStaffed = await draftRoleStaffingResolver(engine.registry);
149270
149338
  res.writeHead(200, { "content-type": "application/json" }).end(
149271
- JSON.stringify({ data: chain.map((d) => ({ ...d, graph: buildWorkorderDraftDetail(d, resolver, chainTypeRv).graph })) })
149339
+ JSON.stringify({ data: chain.map((d) => {
149340
+ const dt = buildWorkorderDraftDetail(d, resolver, chainTypeRv, chainStaffed);
149341
+ return { ...d, graph: dt.graph, ...dt.planning ? { planning: dt.planning } : {} };
149342
+ }) })
149272
149343
  );
149273
149344
  return;
149274
149345
  }
@@ -149277,9 +149348,13 @@ async function startOasisServer(opts) {
149277
149348
  const chains = session ? await opts.workorderDrafts.chainsForSession(session) : [];
149278
149349
  const resolver = await draftActorResolver(engine.registry);
149279
149350
  const chainsTypeRv = await draftTypeReviewerResolver(opts.schema, engine.registry);
149351
+ const chainsStaffed = await draftRoleStaffingResolver(engine.registry);
149280
149352
  res.writeHead(200, { "content-type": "application/json" }).end(
149281
149353
  JSON.stringify({
149282
- data: chains.map((chain) => chain.map((d) => ({ ...d, graph: buildWorkorderDraftDetail(d, resolver, chainsTypeRv).graph })))
149354
+ data: chains.map((chain) => chain.map((d) => {
149355
+ const dt = buildWorkorderDraftDetail(d, resolver, chainsTypeRv, chainsStaffed);
149356
+ return { ...d, graph: dt.graph, ...dt.planning ? { planning: dt.planning } : {} };
149357
+ }))
149283
149358
  })
149284
149359
  );
149285
149360
  return;
@@ -149581,8 +149656,9 @@ async function startOasisServer(opts) {
149581
149656
  }
149582
149657
  draft.plan = plan;
149583
149658
  await opts.workorderDrafts.markEdited(draft.id);
149659
+ const planDetail = buildWorkorderDraftDetail(draft, await draftActorResolver(engine.registry), await draftTypeReviewerResolver(opts.schema, engine.registry), await draftRoleStaffingResolver(engine.registry));
149584
149660
  res.writeHead(200, { "content-type": "application/json" }).end(
149585
- JSON.stringify({ draft: true, workspace: draft.workspace, graph: buildWorkorderDraftDetail(draft, await draftActorResolver(engine.registry), await draftTypeReviewerResolver(opts.schema, engine.registry)).graph })
149661
+ JSON.stringify({ draft: true, workspace: draft.workspace, graph: planDetail.graph, ...planDetail.planning ? { planning: planDetail.planning } : {} })
149586
149662
  );
149587
149663
  } catch (err) {
149588
149664
  res.writeHead(400, { "content-type": "application/json" }).end(
@@ -149621,8 +149697,9 @@ async function startOasisServer(opts) {
149621
149697
  const draft = await opts.workorderDrafts.snapshotForNewRun(draft0, runId);
149622
149698
  editDraftPlan(draft, op);
149623
149699
  await opts.workorderDrafts.markEdited(draft.id);
149700
+ const stageDetail = buildWorkorderDraftDetail(draft, await draftActorResolver(engine.registry), await draftTypeReviewerResolver(opts.schema, engine.registry), await draftRoleStaffingResolver(engine.registry));
149624
149701
  res.writeHead(200, { "content-type": "application/json" }).end(
149625
- JSON.stringify({ draft: true, workspace: draft.workspace, graph: buildWorkorderDraftDetail(draft, await draftActorResolver(engine.registry), await draftTypeReviewerResolver(opts.schema, engine.registry)).graph, staged: op })
149702
+ JSON.stringify({ draft: true, workspace: draft.workspace, graph: stageDetail.graph, ...stageDetail.planning ? { planning: stageDetail.planning } : {}, staged: op })
149626
149703
  );
149627
149704
  return;
149628
149705
  }
@@ -149826,6 +149903,12 @@ async function startOasisServer(opts) {
149826
149903
  try {
149827
149904
  const body = JSON.parse(rawBody);
149828
149905
  if (typeof body.contentBase64 !== "string" || !body.contentBase64) throw new Error("\u7F3A\u5C11 contentBase64");
149906
+ const MAX_BASE64_LEN = Math.ceil(CHAT_ATTACHMENT_MAX_BYTES * 4 / 3) + 4096;
149907
+ if (body.contentBase64.length > MAX_BASE64_LEN) {
149908
+ res.writeHead(413, { "content-type": "application/json" });
149909
+ res.end(JSON.stringify({ error: { code: "ATTACHMENT_TOO_LARGE", message: `\u9644\u4EF6\u8D85\u8FC7 ${CHAT_ATTACHMENT_MAX_BYTES / 1024 / 1024}MB \u4E0A\u9650` } }));
149910
+ return;
149911
+ }
149829
149912
  const out = await opts.uploadChatAttachment({
149830
149913
  contentBase64: body.contentBase64,
149831
149914
  ...typeof body.contentType === "string" ? { contentType: body.contentType } : {},
@@ -151235,9 +151318,11 @@ var MemoryRegistryStore;
151235
151318
  var init_memory_registry_store = __esm({
151236
151319
  "../testkit/src/memory-registry-store.ts"() {
151237
151320
  "use strict";
151321
+ init_src();
151238
151322
  MemoryRegistryStore = class {
151239
151323
  actors = /* @__PURE__ */ new Map();
151240
151324
  configs = /* @__PURE__ */ new Map();
151325
+ agentPrefs = /* @__PURE__ */ new Map();
151241
151326
  teams = /* @__PURE__ */ new Map();
151242
151327
  bindings = /* @__PURE__ */ new Map();
151243
151328
  runtimeConfigs = /* @__PURE__ */ new Map();
@@ -151300,6 +151385,23 @@ var init_memory_registry_store = __esm({
151300
151385
  async listConfigVersions(actorId) {
151301
151386
  return (this.configs.get(actorId) ?? []).map((c) => ({ ...c }));
151302
151387
  }
151388
+ /* ---------- Agent 独立设置(不进版本链;缺行 = 默认) ---------- */
151389
+ async getAgentPrefs(actorId) {
151390
+ const p2 = this.agentPrefs.get(actorId);
151391
+ return p2 ? { ...p2 } : null;
151392
+ }
151393
+ /** 补丁语义与 PG 版一致:没带的项保持原值(缺行则先按默认物化),不是被 undefined 打回默认。 */
151394
+ async setAgentPrefs(actorId, patch, by) {
151395
+ const cur = this.agentPrefs.get(actorId);
151396
+ const next = {
151397
+ actorId,
151398
+ memoryEnabled: patch.memoryEnabled ?? cur?.memoryEnabled ?? AGENT_PREFS_DEFAULTS.memoryEnabled,
151399
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
151400
+ updatedBy: by
151401
+ };
151402
+ this.agentPrefs.set(actorId, next);
151403
+ return { ...next };
151404
+ }
151303
151405
  async upsertTeam(t) {
151304
151406
  this.teams.set(t.id, { ...t });
151305
151407
  }
@@ -158529,7 +158631,14 @@ var init_service3 = __esm({
158529
158631
  const next = {
158530
158632
  actorId,
158531
158633
  version: (latest?.version ?? 0) + 1,
158532
- ...patch,
158634
+ // 每个字段「传了用传的、没传沿用上一版、没有上一版取空」。
158635
+ // 首版落 prompt=""/model="" 是**刻意的中性值**:两处消费点都按 falsy 回落
158636
+ // (identity 段不写、模型走 runtime 默认),与「没有配置」的行为一模一样——
158637
+ // 于是「新员工点一下开关」不会顺手把模型钉死成某个默认值。
158638
+ prompt: patch.prompt ?? latest?.prompt ?? "",
158639
+ model: patch.model ?? latest?.model ?? "",
158640
+ skills: patch.skills ?? latest?.skills ?? [],
158641
+ connectorIds: patch.connectorIds ?? latest?.connectorIds ?? [],
158533
158642
  maxConcurrent: patch.maxConcurrent ?? latest?.maxConcurrent ?? 4,
158534
158643
  batchedContinuation: patch.batchedContinuation ?? latest?.batchedContinuation ?? false,
158535
158644
  createdBy: by,
@@ -158573,6 +158682,24 @@ var init_service3 = __esm({
158573
158682
  listConfigVersions(actorId) {
158574
158683
  return this.opts.store.listConfigVersions(actorId);
158575
158684
  }
158685
+ /* ---------- Agent 独立设置(**不进配置版本链**:翻开关不产人设版本、回滚人设也不动它) ---------- */
158686
+ /**
158687
+ * 读该员工的独立设置。**缺行返回默认**而不是 null——调用方不该各写一遍 `?? true`,
158688
+ * 默认值只在 `AGENT_PREFS_DEFAULTS` 一处定义(读侧唯一来源)。
158689
+ */
158690
+ async getAgentPrefs(actorId) {
158691
+ const row = await this.opts.store.getAgentPrefs(actorId);
158692
+ return row ?? { actorId, ...AGENT_PREFS_DEFAULTS, updatedAt: "", updatedBy: "" };
158693
+ }
158694
+ /** 写独立设置。只对 agent 开放(与 patchConfig 同规),没带的项保持原值。 */
158695
+ async setAgentPrefs(actorId, patch, by) {
158696
+ const actor = await this.opts.store.getActor(actorId);
158697
+ if (!actor) throw new Error(`actor not found: ${actorId}`);
158698
+ if (actor.kind !== "agent") throw new Error(`prefs only apply to agent actors: ${actorId}`);
158699
+ const next = await this.opts.store.setAgentPrefs(actorId, patch, by);
158700
+ await this.audit({ kind: "agent_prefs_patch", actorId, by, at: next.updatedAt, detail: { ...patch } });
158701
+ return next;
158702
+ }
158576
158703
  /* ---------- 团队与组织树(2.4 / 2.5) ---------- */
158577
158704
  upsertTeam(t) {
158578
158705
  return this.opts.store.upsertTeam(t);
@@ -160427,20 +160554,35 @@ function actorsDomain(opts) {
160427
160554
  });
160428
160555
  router.post("/api/actors/:id/config", async (req) => {
160429
160556
  const { service } = await resolveCtx(req.auth.companyId);
160430
- const b2 = req.body;
160431
- if (typeof b2?.prompt !== "string" || typeof b2?.model !== "string") {
160432
- throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 prompt / model");
160433
- }
160434
- if (b2.batchedContinuation !== void 0 && typeof b2.batchedContinuation !== "boolean") {
160435
- throw new ApiError(400, "BAD_REQUEST", "batchedContinuation \u5FC5\u987B\u662F boolean");
160436
- }
160557
+ const b2 = req.body ?? {};
160558
+ const checkString = (key) => {
160559
+ if (b2[key] !== void 0 && typeof b2[key] !== "string") {
160560
+ throw new ApiError(400, "BAD_REQUEST", `${key} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
160561
+ }
160562
+ };
160563
+ const checkStringArray = (key) => {
160564
+ const v2 = b2[key];
160565
+ if (v2 !== void 0 && (!Array.isArray(v2) || v2.some((x2) => typeof x2 !== "string"))) {
160566
+ throw new ApiError(400, "BAD_REQUEST", `${key} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4`);
160567
+ }
160568
+ };
160569
+ const checkBoolean = (key) => {
160570
+ if (b2[key] !== void 0 && typeof b2[key] !== "boolean") {
160571
+ throw new ApiError(400, "BAD_REQUEST", `${key} \u5FC5\u987B\u662F boolean`);
160572
+ }
160573
+ };
160574
+ checkString("prompt");
160575
+ checkString("model");
160576
+ checkStringArray("skills");
160577
+ checkStringArray("connectorIds");
160578
+ checkBoolean("batchedContinuation");
160437
160579
  const cfg = await service.patchConfig(
160438
160580
  req.params.id,
160439
160581
  {
160440
- prompt: b2.prompt,
160441
- model: b2.model,
160442
- skills: b2.skills ?? [],
160443
- connectorIds: b2.connectorIds ?? [],
160582
+ ...b2.prompt !== void 0 ? { prompt: b2.prompt } : {},
160583
+ ...b2.model !== void 0 ? { model: b2.model } : {},
160584
+ ...b2.skills !== void 0 ? { skills: b2.skills } : {},
160585
+ ...b2.connectorIds !== void 0 ? { connectorIds: b2.connectorIds } : {},
160444
160586
  ...typeof b2.maxConcurrent === "number" ? { maxConcurrent: b2.maxConcurrent } : {},
160445
160587
  ...b2.batchedContinuation !== void 0 ? { batchedContinuation: b2.batchedContinuation } : {}
160446
160588
  },
@@ -160459,6 +160601,23 @@ function actorsDomain(opts) {
160459
160601
  const cfg = await service.rollbackConfig(req.params.id, b2.version, req.auth.actor);
160460
160602
  return { status: 201, body: cfg };
160461
160603
  });
160604
+ router.get("/api/actors/:id/settings", async (req) => {
160605
+ const { service } = await resolveCtx(req.auth.companyId);
160606
+ return { status: 200, body: await service.getAgentPrefs(req.params.id) };
160607
+ });
160608
+ router.patch("/api/actors/:id/settings", async (req) => {
160609
+ const { service } = await resolveCtx(req.auth.companyId);
160610
+ const b2 = req.body ?? {};
160611
+ if (b2.memoryEnabled !== void 0 && typeof b2.memoryEnabled !== "boolean") {
160612
+ throw new ApiError(400, "BAD_REQUEST", "memoryEnabled \u5FC5\u987B\u662F boolean");
160613
+ }
160614
+ const prefs = await service.setAgentPrefs(
160615
+ req.params.id,
160616
+ { ...b2.memoryEnabled !== void 0 ? { memoryEnabled: b2.memoryEnabled } : {} },
160617
+ req.auth.actor
160618
+ );
160619
+ return { status: 200, body: prefs };
160620
+ });
160462
160621
  router.get("/api/teams", async (req) => {
160463
160622
  const { service } = await resolveCtx(req.auth.companyId);
160464
160623
  return { status: 200, body: { items: await service.listTeams() } };
@@ -160942,6 +161101,14 @@ function actorsDomain(opts) {
160942
161101
  if (!m2) throw new ApiError(501, "NOT_IMPLEMENTED", "\u8BB0\u5FC6\u5B58\u50A8\u672A\u63A5\u5165\uFF08\u672A\u914D\u7F6E Postgres \u6216\u672A\u542F\u7528\uFF09");
160943
161102
  return m2;
160944
161103
  };
161104
+ const MEMORY_DISABLED_MESSAGE = "\u8BE5\u5458\u5DE5\u7684\u957F\u671F\u8BB0\u5FC6\u529F\u80FD\u5DF2\u88AB\u7BA1\u7406\u8005\u5173\u95ED\uFF0C\u672C\u6B21\u8C03\u7528\u4E0D\u53EF\u7528\u3002\u8FD9\u4E0D\u662F\u6545\u969C\uFF0C\u91CD\u8BD5\u4E0D\u4F1A\u6210\u529F\uFF1A\u672C\u8F6E\u4E0D\u8981\u518D\u5C1D\u8BD5\u4EFB\u4F55 oasis mem-* \u547D\u4EE4\uFF0C\u4E5F\u4E0D\u8981\u4E3A\u6B64\u4E0A\u62A5\u7F3A\u53E3\u6216\u7533\u8BF7\u89E3\u5C01\u3002\u5DF2\u4EA7\u751F\u7684\u8BB0\u5FC6\u6570\u636E\u4ECD\u7136\u4FDD\u7559\uFF0C\u4EBA\u53EF\u5728\u63A7\u5236\u53F0\u7684\u5458\u5DE5\u300C\u8BB0\u5FC6\u300D\u9875\u67E5\u770B\u3002";
161105
+ const memoryApi = async (req) => {
161106
+ const svc = requireMemory();
161107
+ const { service } = await resolveCtx(req.auth.companyId);
161108
+ const prefs = await service.getAgentPrefs(req.auth.actor);
161109
+ if (prefs.memoryEnabled === false) throw new ApiError(403, "MEMORY_DISABLED", MEMORY_DISABLED_MESSAGE);
161110
+ return { svc, ctx: memoryCtx(req) };
161111
+ };
160945
161112
  const parseKeywords = (raw) => (raw ?? "").split(",").map((k2) => k2.trim()).filter((k2) => k2.length > 0);
160946
161113
  const memoryPage = (req) => {
160947
161114
  const parseInteger = (name, fallback) => {
@@ -161121,7 +161288,8 @@ function actorsDomain(opts) {
161121
161288
  const offsetRaw = req.query.get("offset");
161122
161289
  const limit = limitRaw && Number.isFinite(Number(limitRaw)) ? Math.min(Math.max(Number(limitRaw), 1), 200) : 50;
161123
161290
  const offset = offsetRaw && Number.isFinite(Number(offsetRaw)) ? Math.max(Number(offsetRaw), 0) : 0;
161124
- const out = await requireMemory().list(memoryCtx(req), {
161291
+ const { svc, ctx } = await memoryApi(req);
161292
+ const out = await svc.list(ctx, {
161125
161293
  scope: parseReadScope(req.query.get("scope")),
161126
161294
  keywords: parseKeywords(req.query.get("keywords")),
161127
161295
  limit,
@@ -161132,7 +161300,8 @@ function actorsDomain(opts) {
161132
161300
  router.get("/api/actor-memory/search", async (req) => {
161133
161301
  const topKRaw = req.query.get("top_k");
161134
161302
  const topK = topKRaw && Number.isFinite(Number(topKRaw)) ? Math.min(Math.max(Number(topKRaw), 1), 50) : 5;
161135
- const out = await requireMemory().legacySearch(memoryCtx(req), {
161303
+ const { svc, ctx } = await memoryApi(req);
161304
+ const out = await svc.legacySearch(ctx, {
161136
161305
  keywords: parseKeywords(req.query.get("keywords")),
161137
161306
  scope: parseReadScope(req.query.get("scope")),
161138
161307
  topK
@@ -161141,7 +161310,8 @@ function actorsDomain(opts) {
161141
161310
  });
161142
161311
  router.get("/api/actor-memory/*", async (req) => {
161143
161312
  const ids = (req.params["*"] ?? "").split(",").map((s2) => s2.trim()).filter((s2) => s2.length > 0);
161144
- const items = await requireMemory().read(memoryCtx(req), ids);
161313
+ const { svc, ctx } = await memoryApi(req);
161314
+ const items = await svc.read(ctx, ids);
161145
161315
  return { status: 200, body: { items } };
161146
161316
  });
161147
161317
  router.post("/api/actor-memory", async (req) => {
@@ -161152,8 +161322,9 @@ function actorsDomain(opts) {
161152
161322
  if (b2.expectedVersion !== void 0 && typeof b2.expectedVersion !== "number") {
161153
161323
  throw new ApiError(400, "BAD_REQUEST", "expectedVersion \u5FC5\u987B\u662F\u6570\u5B57");
161154
161324
  }
161325
+ const { svc, ctx } = await memoryApi(req);
161155
161326
  try {
161156
- const rec = await requireMemory().write(memoryCtx(req), {
161327
+ const rec = await svc.write(ctx, {
161157
161328
  scope: parseWriteScope(b2.scope),
161158
161329
  content: b2.content,
161159
161330
  keywords: (b2.keywords ?? []).map((k2) => String(k2)),
@@ -161175,7 +161346,8 @@ function actorsDomain(opts) {
161175
161346
  }
161176
161347
  });
161177
161348
  router.delete("/api/actor-memory/*", async (req) => {
161178
- await requireMemory().remove(memoryCtx(req), req.params["*"] ?? "");
161349
+ const { svc, ctx } = await memoryApi(req);
161350
+ await svc.remove(ctx, req.params["*"] ?? "");
161179
161351
  return { status: 200, body: { ok: true } };
161180
161352
  });
161181
161353
  };
@@ -177209,11 +177381,12 @@ function rowToVariable(row) {
177209
177381
  updatedAt: new Date(row.updated_at).toISOString()
177210
177382
  };
177211
177383
  }
177212
- var ident4, PostgresRegistryStore, rowToActor, rowToConfig;
177384
+ var ident4, PostgresRegistryStore, rowToActor, rowToConfig, rowToAgentPrefs;
177213
177385
  var init_postgres_registry = __esm({
177214
177386
  "../storage/src/postgres-registry.ts"() {
177215
177387
  "use strict";
177216
177388
  init_esm2();
177389
+ init_src();
177217
177390
  ident4 = (s2) => {
177218
177391
  if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
177219
177392
  return s2;
@@ -177251,6 +177424,9 @@ var init_postgres_registry = __esm({
177251
177424
  reviewer_ids jsonb NOT NULL DEFAULT '[]'::jsonb,
177252
177425
  max_concurrent integer NOT NULL DEFAULT 4,
177253
177426
  batched_continuation boolean NOT NULL DEFAULT false,
177427
+ -- memory_enabled \u5217\u4FDD\u7559\uFF082026-08-10 \u65E9\u5148\u7248\u672C\u5199\u8FC7\uFF09\u4F46\u4E0D\u518D\u8BFB\u5199\uFF1A\u8BB0\u5FC6\u5F00\u5173\u5DF2\u632A\u5230
177428
+ -- agent_prefs\uFF08\u72EC\u7ACB\u8BBE\u7F6E\uFF0C\u4E0D\u8FDB\u7248\u672C\u94FE\uFF09\u3002\u4E0E reviewer_ids \u540C\u89C4\u683C\u7684\u6B7B\u5217\u3002
177429
+ memory_enabled boolean NOT NULL DEFAULT true,
177254
177430
  created_by text NOT NULL,
177255
177431
  created_at timestamptz NOT NULL,
177256
177432
  source text NOT NULL, -- patch | rollback
@@ -177260,6 +177436,23 @@ var init_postgres_registry = __esm({
177260
177436
  await pool.query(`ALTER TABLE "${s2}".actor_configs ADD COLUMN IF NOT EXISTS max_concurrent integer NOT NULL DEFAULT 4`);
177261
177437
  await pool.query(`ALTER TABLE "${s2}".actor_configs ADD COLUMN IF NOT EXISTS reviewer_ids jsonb NOT NULL DEFAULT '[]'::jsonb`);
177262
177438
  await pool.query(`ALTER TABLE "${s2}".actor_configs ADD COLUMN IF NOT EXISTS batched_continuation boolean NOT NULL DEFAULT false`);
177439
+ await pool.query(`ALTER TABLE "${s2}".actor_configs ADD COLUMN IF NOT EXISTS memory_enabled boolean NOT NULL DEFAULT true`);
177440
+ await pool.query(`
177441
+ CREATE TABLE IF NOT EXISTS "${s2}".agent_prefs (
177442
+ actor_id text PRIMARY KEY,
177443
+ memory_enabled boolean NOT NULL DEFAULT true,
177444
+ updated_at timestamptz NOT NULL,
177445
+ updated_by text NOT NULL
177446
+ )`);
177447
+ await pool.query(`
177448
+ INSERT INTO "${s2}".agent_prefs (actor_id, memory_enabled, updated_at, updated_by)
177449
+ SELECT actor_id, false, now(), 'actor:system:migration'
177450
+ FROM (
177451
+ SELECT DISTINCT ON (actor_id) actor_id, memory_enabled
177452
+ FROM "${s2}".actor_configs ORDER BY actor_id, version DESC
177453
+ ) latest
177454
+ WHERE latest.memory_enabled = false
177455
+ ON CONFLICT (actor_id) DO NOTHING`);
177263
177456
  await pool.query(`ALTER TABLE "${s2}".actors ADD COLUMN IF NOT EXISTS reports_to text`);
177264
177457
  await pool.query(`ALTER TABLE "${s2}".actors ADD COLUMN IF NOT EXISTS description text`);
177265
177458
  await pool.query(`ALTER TABLE "${s2}".actors ADD COLUMN IF NOT EXISTS tags jsonb NOT NULL DEFAULT '[]'::jsonb`);
@@ -177532,6 +177725,27 @@ var init_postgres_registry = __esm({
177532
177725
  );
177533
177726
  return r.rows.map(rowToConfig);
177534
177727
  }
177728
+ /* ---------- Agent 独立设置(不进版本链) ---------- */
177729
+ async getAgentPrefs(actorId) {
177730
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.agent_prefs WHERE actor_id = $1`, [actorId]);
177731
+ return r.rows[0] ? rowToAgentPrefs(r.rows[0]) : null;
177732
+ }
177733
+ /**
177734
+ * upsert 补丁。`COALESCE($2, agent_prefs.memory_enabled)` 是关键:补丁没带的项**保持库里那份**,
177735
+ * 不是被 NULL 覆盖——否则将来加第二个开关时,改 A 会顺手把 B 打回默认。
177736
+ */
177737
+ async setAgentPrefs(actorId, patch, by) {
177738
+ const r = await this.pool.query(
177739
+ `INSERT INTO ${this.s}.agent_prefs (actor_id, memory_enabled, updated_at, updated_by)
177740
+ VALUES ($1, COALESCE($2::boolean, $5::boolean), $3, $4)
177741
+ ON CONFLICT (actor_id) DO UPDATE SET
177742
+ memory_enabled = COALESCE($2::boolean, ${this.s}.agent_prefs.memory_enabled),
177743
+ updated_at = $3, updated_by = $4
177744
+ RETURNING *`,
177745
+ [actorId, patch.memoryEnabled ?? null, (/* @__PURE__ */ new Date()).toISOString(), by, AGENT_PREFS_DEFAULTS.memoryEnabled]
177746
+ );
177747
+ return rowToAgentPrefs(r.rows[0]);
177748
+ }
177535
177749
  /* ---------- 团队 ---------- */
177536
177750
  async upsertTeam(t) {
177537
177751
  await this.pool.query(
@@ -177885,11 +178099,18 @@ var init_postgres_registry = __esm({
177885
178099
  connectorIds: row.connector_ids,
177886
178100
  maxConcurrent: row.max_concurrent ?? 4,
177887
178101
  batchedContinuation: row.batched_continuation ?? false,
178102
+ // 注意:memory_enabled 是死列,不映射进 ActorConfig——记忆开关在 agent_prefs。
177888
178103
  createdBy: row.created_by,
177889
178104
  createdAt: new Date(row.created_at).toISOString(),
177890
178105
  source: row.source,
177891
178106
  ...row.rolled_back_from !== null ? { rolledBackFrom: row.rolled_back_from } : {}
177892
178107
  });
178108
+ rowToAgentPrefs = (row) => ({
178109
+ actorId: row.actor_id,
178110
+ memoryEnabled: row.memory_enabled ?? AGENT_PREFS_DEFAULTS.memoryEnabled,
178111
+ updatedAt: new Date(row.updated_at).toISOString(),
178112
+ updatedBy: row.updated_by
178113
+ });
177893
178114
  }
177894
178115
  });
177895
178116
 
@@ -183013,15 +183234,24 @@ async function buildActorProvision(service, actorId, scope, onBrokerRefused) {
183013
183234
  };
183014
183235
  }
183015
183236
  async function buildActorContext(service, actorId) {
183016
- const [config2, connectors, installedSkills, record6] = await Promise.all([
183237
+ const [config2, connectors, installedSkills, record6, prefs] = await Promise.all([
183017
183238
  service.latestConfig(actorId),
183018
183239
  service.listConnectors(),
183019
183240
  service.listInstalledSkillsForActor(actorId),
183020
183241
  // ADR-0095 D2:执行者**本人**的岗位——评审派发要靠它告诉 reviewer「你是谁」
183021
183242
  // (不能拿被审产物的 ownerRole 冒充)。查不到不致命,assembler 会退回原行为。
183022
- service.getActor(actorId).catch(() => null)
183243
+ service.getActor(actorId).catch(() => null),
183244
+ // 独立设置(记忆总开关等)——**不在 config 里**,另取一次;失败按默认(全开)走,
183245
+ // 一次设置查询挂掉不该让整轮派单变成「什么都关了」。
183246
+ service.getAgentPrefs(actorId).catch(() => null)
183023
183247
  ]);
183024
- return { config: config2, connectors, installedSkills, ...record6?.roles?.length ? { roles: record6.roles } : {} };
183248
+ return {
183249
+ config: config2,
183250
+ connectors,
183251
+ installedSkills,
183252
+ ...record6?.roles?.length ? { roles: record6.roles } : {},
183253
+ ...prefs ? { memoryEnabled: prefs.memoryEnabled } : {}
183254
+ };
183025
183255
  }
183026
183256
  async function buildChatWhoSection(service, rolesAdmin, humanActorId, selfActorId) {
183027
183257
  if (!humanActorId) return [];
@@ -183046,14 +183276,15 @@ async function buildChatWhoSection(service, rolesAdmin, humanActorId, selfActorI
183046
183276
  lines.push(`\u60F3\u8FDB\u4E00\u6B65\u4E86\u89E3 TA\u3001\u6216\u6838\u5B9E\u5BF9\u8BDD\u91CC\u63D0\u5230\u7684\u5176\u4ED6\u4EBA\u662F\u8C01\uFF1A\`oasis actor <actorId>\`\uFF08\u5355\u4EBA\u5B8C\u6574\u6863\u6848\uFF09\u3002`);
183047
183277
  return lines;
183048
183278
  }
183049
- var CHAT_FINAL_REPLY_RULE = [
183279
+ var chatFinalReplyRule = (opts) => [
183050
183280
  "",
183051
183281
  "## \u6536\u5C3E\u987A\u5E8F\uFF08\u5C55\u793A\u9762\u5951\u7EA6\uFF09",
183052
183282
  "",
183053
183283
  "**\u4F60\u7684\u6700\u540E\u4E00\u6BB5\u6B63\u6587\u5C31\u662F\u7528\u6237\u770B\u5230\u7684\u56DE\u590D**\u2014\u2014\u5728\u5B83\u4E4B\u524D\u7684\u5185\u5BB9\uFF0C\u754C\u9762\u9ED8\u8BA4\u6298\u53E0\u6210\u300C\u601D\u8003\u8FC7\u7A0B\u300D\u3002",
183054
- "\u6240\u4EE5\uFF1A\u6240\u6709\u6536\u5C3E\u526F\u4F5C\u7528\uFF08`oasis mem-write`\u3001\u6539\u56FE / \u5EFA\u5355\u3001\u53D1\u6D88\u606F\u3001\u5199\u6587\u4EF6\uFF09\u90FD\u5728\u7ED9\u51FA\u56DE\u590D**\u4E4B\u524D**\u505A\u5B8C\uFF1B",
183284
+ `\u6240\u4EE5\uFF1A\u6240\u6709\u6536\u5C3E\u526F\u4F5C\u7528\uFF08${opts.memoryEnabled ? "`oasis mem-write`\u3001" : ""}\u6539\u56FE / \u5EFA\u5355\u3001\u53D1\u6D88\u606F\u3001\u5199\u6587\u4EF6\uFF09\u90FD\u5728\u7ED9\u51FA\u56DE\u590D**\u4E4B\u524D**\u505A\u5B8C\uFF1B`,
183055
183285
  "\u505A\u5B8C\u4E4B\u540E\u522B\u518D\u8865\u300C\u5DF2\u4FDD\u5B58 / \u5DF2\u8BB0\u5F55 / \u597D\u7684\u300D\u8FD9\u7C7B\u786E\u8BA4\u53E5\u2014\u2014\u8981\u8BF4\u7684\u4E00\u5E76\u5199\u8FDB\u90A3\u6BB5\u56DE\u590D\u6B63\u6587\u91CC\u3002"
183056
183286
  ];
183287
+ var CHAT_FINAL_REPLY_RULE = chatFinalReplyRule({ memoryEnabled: true });
183057
183288
  function traceRuntimeKind(runtimeKind) {
183058
183289
  if (runtimeKind === "claude" || runtimeKind === "claude-code") return "claude-code";
183059
183290
  if (runtimeKind === "codex") return "codex";
@@ -184191,7 +184422,8 @@ async function startServe(opts) {
184191
184422
  "",
184192
184423
  "\u8BF7\u76F4\u63A5\u56DE\u7B54\u7528\u6237\u95EE\u9898\u3002\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u5199\u5165\u7CFB\u7EDF\uFF0C\u5426\u5219\u4E0D\u8981\u521B\u5EFA\u6216\u4FEE\u6539 artifact\u3002"
184193
184424
  ];
184194
- const chatMemoryLines = actorMemoryService ? await actorMemoryService.list({ actorId }, { scope: "all", keywords: ngramQueryTerms([message]), limit: MEMORY_INJECT_MAX, offset: 0 }).then((out) => [...memoryIndexLines(out.items), ...memoryHowToLines()]).catch(() => []) : [];
184425
+ const chatMemoryEnabled = actorCtx?.memoryEnabled !== false;
184426
+ const chatMemoryLines = actorMemoryService && chatMemoryEnabled ? await actorMemoryService.list({ actorId }, { scope: "all", keywords: ngramQueryTerms([message]), limit: MEMORY_INJECT_MAX, offset: 0 }).then((out) => [...memoryIndexLines(out.items), ...memoryHowToLines()]).catch(() => []) : [];
184195
184427
  const files = {
184196
184428
  "TASK.md": [
184197
184429
  ...taskHeader,
@@ -184205,7 +184437,7 @@ async function startServe(opts) {
184205
184437
  message,
184206
184438
  ...chatMemoryLines,
184207
184439
  // 压在最末:这条要盖住紧邻其上的「收工前想一下」,否则记忆写入就落到正文之后(见常量注释)。
184208
- ...CHAT_FINAL_REPLY_RULE
184440
+ ...chatFinalReplyRule({ memoryEnabled: chatMemoryEnabled })
184209
184441
  ].join("\n")
184210
184442
  };
184211
184443
  Object.assign(files, attachmentFiles);
@@ -188052,6 +188284,107 @@ function makeClient(base, token) {
188052
188284
  }
188053
188285
  };
188054
188286
  }
188287
+ var STAGE_COMMANDS = /* @__PURE__ */ new Set(["link", "spawn", "unlink", "cancel", "freeze", "cancel-part", "edit", "assign", "reopen"]);
188288
+ function fieldsFromFlags(flags, ownFlags) {
188289
+ const fields = {};
188290
+ for (const [k2, v2] of flags) {
188291
+ if (ownFlags.has(k2) || GLOBAL_FLAGS.has(k2) || BEHAVIORAL_FLAGS.has(k2)) continue;
188292
+ fields[k2] = v2;
188293
+ }
188294
+ const sj = flags.get("set-json");
188295
+ if (sj !== void 0) {
188296
+ const parsed = JSON.parse(sj);
188297
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
188298
+ throw new Error(`--set-json \u9700\u8981\u4E00\u4E2A JSON \u5BF9\u8C61\uFF0C\u5982 '{"goal":"..."}'`);
188299
+ }
188300
+ Object.assign(fields, parsed);
188301
+ }
188302
+ const clear = flags.get("clear");
188303
+ if (clear !== void 0) for (const k2 of clear.split(",")) {
188304
+ const t = k2.trim();
188305
+ if (t) fields[t] = null;
188306
+ }
188307
+ return Object.keys(fields).length > 0 ? fields : void 0;
188308
+ }
188309
+ function buildStageOp(cmd, flags, positional, readFile4 = (file) => fs33.readFileSync(file, "utf8")) {
188310
+ switch (cmd) {
188311
+ case "link":
188312
+ return {
188313
+ action: "link",
188314
+ artifactId: needPos(positional, 0, "oasis link <consumer> --to <upstream>"),
188315
+ to: need(flags, "to"),
188316
+ ...flags.get("required") !== void 0 ? { required: flags.get("required") === "true" } : {}
188317
+ };
188318
+ case "spawn": {
188319
+ const fields = fieldsFromFlags(flags, ownFlagsFromDecl("spawn"));
188320
+ const spawnId = positional[0];
188321
+ const partsFile = flags.get("parts-file");
188322
+ return {
188323
+ action: "spawn",
188324
+ ...spawnId ? { id: spawnId } : {},
188325
+ type: need(flags, "type"),
188326
+ ...flags.get("owner") !== void 0 ? { owner: flags.get("owner") } : {},
188327
+ ...flags.get("persistence") !== void 0 ? { persistence: flags.get("persistence") } : {},
188328
+ ...flags.get("title") !== void 0 ? { title: flags.get("title") } : {},
188329
+ ...(flags.get("brief") ?? flags.get("description")) !== void 0 ? { description: flags.get("brief") ?? flags.get("description") } : {},
188330
+ ...flags.get("input") !== void 0 ? { inputs: flags.get("input").split(",").map((to) => ({ to: to.trim() })) } : {},
188331
+ ...partsFile !== void 0 ? { parts: JSON.parse(readFile4(partsFile)) } : {},
188332
+ ...fields !== void 0 ? { fields } : {}
188333
+ };
188334
+ }
188335
+ case "unlink":
188336
+ return { action: "unlink", artifactId: needPos(positional, 0, "oasis unlink <consumer> --to <upstream>"), to: need(flags, "to") };
188337
+ case "cancel":
188338
+ case "freeze":
188339
+ return {
188340
+ action: "seal",
188341
+ artifactId: needPos(positional, 0, `oasis ${cmd} <id> [--note \u2026]`),
188342
+ reason: cmd === "cancel" ? "cancelled" : "frozen",
188343
+ ...flags.get("note") !== void 0 ? { note: flags.get("note") } : {}
188344
+ };
188345
+ case "cancel-part":
188346
+ return { action: "cancelPart", artifactId: needPos(positional, 0, "oasis cancel-part <id> --part <name>"), part: need(flags, "part") };
188347
+ case "assign":
188348
+ return { action: "assign", artifactId: needPos(positional, 0, "oasis assign <id> --owner <actorId>"), owner: need(flags, "owner") };
188349
+ case "reopen":
188350
+ return { action: "reopen", artifactId: needPos(positional, 0, "oasis reopen <id> [--note <\u539F\u56E0>]"), ...flags.get("note") !== void 0 ? { note: flags.get("note") } : {} };
188351
+ case "edit": {
188352
+ const fields = fieldsFromFlags(flags, ownFlagsFromDecl("edit"));
188353
+ return {
188354
+ action: "edit",
188355
+ artifactId: needPos(positional, 0, "oasis edit <id> [--title \u2026] [--brief \u2026] [--<\u5B57\u6BB5> <\u503C>] [--set-json <json>] [--clear <\u5B57\u6BB5,\u2026>]\uFF08--<\u5B57\u6BB5> \u662F\u8BE5\u7C7B\u578B\u52A8\u6001\u58F0\u660E\u7684\uFF0C\u7528 oasis artifact-types show <\u8BE5\u4EA7\u7269 id> \u770B\u53EF\u586B\u5B57\u6BB5\uFF09"),
188356
+ ...flags.get("title") !== void 0 ? { title: flags.get("title") } : {},
188357
+ ...flags.get("brief") !== void 0 ? { description: flags.get("brief") } : {},
188358
+ ...fields !== void 0 ? { fields } : {}
188359
+ };
188360
+ }
188361
+ default:
188362
+ throw new Error(`\u672A\u77E5\u6682\u5B58\u547D\u4EE4: ${cmd}`);
188363
+ }
188364
+ }
188365
+ function describeStageOp(op) {
188366
+ switch (op["action"]) {
188367
+ case "link":
188368
+ return `link ${op["artifactId"]} \u2192 ${op["to"]}`;
188369
+ case "spawn":
188370
+ return op["id"] ? `spawn ${op["id"]}\uFF08type=${op["type"]}${op["title"] ? `\uFF0Ctitle=${op["title"]}` : ""}${op["owner"] ? `\uFF0Cowner=${op["owner"]}` : ""}\uFF09` : `spawn ${op["type"]}:${op["title"] ?? "?"}\uFF08\u65B0\u8282\u70B9\uFF0Cid \u843D\u5730\u65F6\u7531\u670D\u52A1\u7AEF\u6D3E\u751F${op["owner"] ? `\uFF0Cowner=${op["owner"]}` : ""}\uFF09`;
188371
+ // id-less spawn:本地无 id,别显 undefined
188372
+ case "unlink":
188373
+ return `unlink ${op["artifactId"]} \u2715 ${op["to"]}`;
188374
+ case "seal":
188375
+ return `${op["reason"] === "cancelled" ? "cancel" : op["reason"] === "frozen" ? "freeze" : "accept"} ${op["artifactId"]}`;
188376
+ case "cancelPart":
188377
+ return `cancel-part ${op["artifactId"]} part=${op["part"]}`;
188378
+ case "assign":
188379
+ return `assign ${op["artifactId"]} \u2192 owner=${op["owner"]}`;
188380
+ case "reopen":
188381
+ return `reopen ${op["artifactId"]}\uFF08\u89E3\u5C01 \u2192 active\uFF09`;
188382
+ case "edit":
188383
+ return `edit ${op["artifactId"]}\uFF08${["title", "description"].filter((k2) => op[k2] !== void 0).join("/") || "?"}\uFF09`;
188384
+ default:
188385
+ return JSON.stringify(op);
188386
+ }
188387
+ }
188055
188388
  async function runCli(argv, println = console.log) {
188056
188389
  const [command, ...restArgs] = argv;
188057
188390
  if (command === "connector-effect-run") {
@@ -188447,109 +188780,12 @@ async function runCli(argv, println = console.log) {
188447
188780
  }
188448
188781
  return uploaded;
188449
188782
  };
188450
- const STAGE_COMMANDS = /* @__PURE__ */ new Set(["link", "spawn", "unlink", "cancel", "freeze", "cancel-part", "edit", "assign", "reopen"]);
188451
- const fieldsFromFlags = (ownFlags) => {
188452
- const fields = {};
188453
- for (const [k2, v2] of flags) {
188454
- if (ownFlags.has(k2) || GLOBAL_FLAGS.has(k2) || BEHAVIORAL_FLAGS.has(k2)) continue;
188455
- fields[k2] = v2;
188456
- }
188457
- const sj = flags.get("set-json");
188458
- if (sj !== void 0) {
188459
- const parsed = JSON.parse(sj);
188460
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
188461
- throw new Error(`--set-json \u9700\u8981\u4E00\u4E2A JSON \u5BF9\u8C61\uFF0C\u5982 '{"goal":"..."}'`);
188462
- }
188463
- Object.assign(fields, parsed);
188464
- }
188465
- const clear = flags.get("clear");
188466
- if (clear !== void 0) for (const k2 of clear.split(",")) {
188467
- const t = k2.trim();
188468
- if (t) fields[t] = null;
188469
- }
188470
- return Object.keys(fields).length > 0 ? fields : void 0;
188471
- };
188472
- const buildStageOp = (cmd) => {
188473
- switch (cmd) {
188474
- case "link":
188475
- return {
188476
- action: "link",
188477
- artifactId: needPos(positional, 0, "oasis link <consumer> --to <upstream>"),
188478
- to: need(flags, "to"),
188479
- ...flags.get("required") !== void 0 ? { required: flags.get("required") === "true" } : {}
188480
- };
188481
- case "spawn": {
188482
- const fields = fieldsFromFlags(ownFlagsFromDecl("spawn"));
188483
- const spawnId = positional[0];
188484
- return {
188485
- action: "spawn",
188486
- ...spawnId ? { id: spawnId } : {},
188487
- type: need(flags, "type"),
188488
- ...flags.get("title") !== void 0 ? { title: flags.get("title") } : {},
188489
- ...(flags.get("brief") ?? flags.get("description")) !== void 0 ? { description: flags.get("brief") ?? flags.get("description") } : {},
188490
- ...flags.get("input") !== void 0 ? { inputs: flags.get("input").split(",").map((to) => ({ to: to.trim() })) } : {},
188491
- ...fields !== void 0 ? { fields } : {}
188492
- };
188493
- }
188494
- case "unlink":
188495
- return { action: "unlink", artifactId: needPos(positional, 0, "oasis unlink <consumer> --to <upstream>"), to: need(flags, "to") };
188496
- case "cancel":
188497
- case "freeze":
188498
- return {
188499
- action: "seal",
188500
- artifactId: needPos(positional, 0, `oasis ${cmd} <id> [--note \u2026]`),
188501
- reason: cmd === "cancel" ? "cancelled" : "frozen",
188502
- ...flags.get("note") !== void 0 ? { note: flags.get("note") } : {}
188503
- };
188504
- case "cancel-part":
188505
- return { action: "cancelPart", artifactId: needPos(positional, 0, "oasis cancel-part <id> --part <name>"), part: need(flags, "part") };
188506
- case "assign":
188507
- return { action: "assign", artifactId: needPos(positional, 0, "oasis assign <id> --owner <actorId>"), owner: need(flags, "owner") };
188508
- case "reopen":
188509
- return { action: "reopen", artifactId: needPos(positional, 0, "oasis reopen <id> [--note <\u539F\u56E0>]"), ...flags.get("note") !== void 0 ? { note: flags.get("note") } : {} };
188510
- case "edit": {
188511
- const fields = fieldsFromFlags(ownFlagsFromDecl("edit"));
188512
- return {
188513
- action: "edit",
188514
- artifactId: needPos(positional, 0, "oasis edit <id> [--title \u2026] [--brief \u2026] [--<\u5B57\u6BB5> <\u503C>] [--set-json <json>] [--clear <\u5B57\u6BB5,\u2026>]\uFF08--<\u5B57\u6BB5> \u662F\u8BE5\u7C7B\u578B\u52A8\u6001\u58F0\u660E\u7684\uFF0C\u7528 oasis artifact-types show <\u8BE5\u4EA7\u7269 id> \u770B\u53EF\u586B\u5B57\u6BB5\uFF09"),
188515
- ...flags.get("title") !== void 0 ? { title: flags.get("title") } : {},
188516
- ...flags.get("brief") !== void 0 ? { description: flags.get("brief") } : {},
188517
- ...fields !== void 0 ? { fields } : {}
188518
- };
188519
- }
188520
- default:
188521
- throw new Error(`\u672A\u77E5\u6682\u5B58\u547D\u4EE4: ${cmd}`);
188522
- }
188523
- };
188524
- const describeStageOp = (op) => {
188525
- switch (op["action"]) {
188526
- case "link":
188527
- return `link ${op["artifactId"]} \u2192 ${op["to"]}`;
188528
- case "spawn":
188529
- return op["id"] ? `spawn ${op["id"]}\uFF08type=${op["type"]}${op["title"] ? `\uFF0Ctitle=${op["title"]}` : ""}\uFF09` : `spawn ${op["type"]}:${op["title"] ?? "?"}\uFF08\u65B0\u8282\u70B9\uFF0Cid \u843D\u5730\u65F6\u7531\u670D\u52A1\u7AEF\u6D3E\u751F\uFF09`;
188530
- // id-less spawn:本地无 id,别显 undefined
188531
- case "unlink":
188532
- return `unlink ${op["artifactId"]} \u2715 ${op["to"]}`;
188533
- case "seal":
188534
- return `${op["reason"] === "cancelled" ? "cancel" : op["reason"] === "frozen" ? "freeze" : "accept"} ${op["artifactId"]}`;
188535
- case "cancelPart":
188536
- return `cancel-part ${op["artifactId"]} part=${op["part"]}`;
188537
- case "assign":
188538
- return `assign ${op["artifactId"]} \u2192 owner=${op["owner"]}`;
188539
- case "reopen":
188540
- return `reopen ${op["artifactId"]}\uFF08\u89E3\u5C01 \u2192 active\uFF09`;
188541
- case "edit":
188542
- return `edit ${op["artifactId"]}\uFF08${["title", "description"].filter((k2) => op[k2] !== void 0).join("/") || "?"}\uFF09`;
188543
- default:
188544
- return JSON.stringify(op);
188545
- }
188546
- };
188547
188783
  const printGraph = (graph) => {
188548
188784
  if (!graph) return;
188549
188785
  for (const line of renderGraphLines(graph)) println(line);
188550
188786
  };
188551
188787
  if (process.env["OASIS_STAGE"] === "1" && STAGE_COMMANDS.has(command)) {
188552
- const op = buildStageOp(command);
188788
+ const op = buildStageOp(command, flags, positional, readFileArg);
188553
188789
  const editWs = flags.get("workspace") ?? process.env["OASIS_WORKSPACE"];
188554
188790
  const r = await api.post("/api/stage", { op, ...editWs ? { workspace: editWs } : {} });
188555
188791
  if (r.draft) {
@@ -189103,7 +189339,7 @@ ${res.warning}`);
189103
189339
  }
189104
189340
  case "spawn": {
189105
189341
  const partsFile = flags.get("parts-file");
189106
- const spawnFields = fieldsFromFlags(ownFlagsFromDecl("spawn"));
189342
+ const spawnFields = fieldsFromFlags(flags, ownFlagsFromDecl("spawn"));
189107
189343
  const { message } = await api.cmd("spawn", {
189108
189344
  id: positional[0],
189109
189345
  type: need(flags, "type"),
@@ -190326,7 +190562,7 @@ function syncRuntimeAssets(candidateRoots, binDir) {
190326
190562
  }
190327
190563
 
190328
190564
  // src/index.ts
190329
- var PKG_VERSION = true ? "0.1.93" : "dev";
190565
+ var PKG_VERSION = true ? "0.1.94" : "dev";
190330
190566
  var OASIS_DIR = path29.join(os10.homedir(), ".oasis");
190331
190567
  var CONFIG_FILE = path29.join(OASIS_DIR, "node-config.json");
190332
190568
  var PID_FILE = path29.join(OASIS_DIR, "node.pid");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test",
3
- "version": "0.1.93",
3
+ "version": "0.1.94",
4
4
  "description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
5
5
  "bin": {
6
6
  "oasis": "./dist/index.js"
@@ -26,6 +26,6 @@
26
26
  "node": ">=20"
27
27
  },
28
28
  "oasisRelease": {
29
- "sourceHead": "f529e39d68297fc8219978cde48c3441e56764a7"
29
+ "sourceHead": "55c83602f72d8ffad16c8cc68a44ba3ed58f91d1"
30
30
  }
31
31
  }