@zq-silk/yui 0.13.6 → 0.13.8

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 (36) hide show
  1. package/ARCHITECTURE.md +52 -17
  2. package/README.md +32 -12
  3. package/dist/cli/updatePorts.js +4 -4
  4. package/dist/cli.js +1 -1
  5. package/dist/commands/taskCommands.js +1 -12
  6. package/dist/commands/taskContextCommand.js +1 -1
  7. package/dist/commands/taskExecutionCommands.js +0 -5
  8. package/dist/commands/taskOverviewCommand.js +5 -1
  9. package/dist/commands/taskRoleRuntimeStatus.js +0 -13
  10. package/dist/context/sessionBootstrapManifest.js +26 -23
  11. package/dist/controller/controller.js +0 -2
  12. package/dist/controller/fileSchedulerStoreAdapter.js +84 -78
  13. package/dist/executor/agentAdapter.js +6 -2
  14. package/dist/executor/agentExecutor.js +14 -28
  15. package/dist/executor/fileRoleLaunchPlanner.js +5 -5
  16. package/dist/lifecycle/exactRunTerminalization.js +1 -3
  17. package/dist/output/rolePresentation.js +0 -1
  18. package/dist/repository/taskWorkspacePreparer.js +0 -1
  19. package/dist/role/role.js +12 -20
  20. package/dist/runtime/agentHost.js +2 -2
  21. package/dist/runtime/builtinAgentDrivers.js +2 -1
  22. package/dist/runtime/index.js +1 -1
  23. package/dist/runtime/launchBroker.js +1 -1
  24. package/dist/runtime/providerRuntimeIdentity.js +8 -17
  25. package/dist/runtime/structuredProviderHost.js +172 -33
  26. package/dist/runtime/tmuxAdapters.js +9 -1
  27. package/dist/scheduler/activeRoleRunDelivery.js +2 -10
  28. package/dist/scheduler/activeTaskProgress.js +1 -4
  29. package/dist/scheduler/leaderWakeupProcessor.js +0 -5
  30. package/dist/scheduler/roleRunStall.js +3 -3
  31. package/dist/storage/migration/productionRegistry.js +143 -0
  32. package/dist/storage/taskStore.js +2 -2
  33. package/dist/web/webSnapshot.js +3 -0
  34. package/i18n/README.zh-CN.md +15 -7
  35. package/package.json +1 -1
  36. package/skills/yui-runtime/SKILL.md +5 -4
@@ -99,10 +99,14 @@ const RELEASE_WORKFLOW_FROM_VERSION = 0;
99
99
  const RELEASE_WORKFLOW_TO_VERSION = 1;
100
100
  const WORK_MAILBOX_FROM_VERSION = 1;
101
101
  const WORK_MAILBOX_TO_VERSION = 2;
102
+ const TASK_ROLE_FROM_VERSION = 3;
103
+ const TASK_ROLE_TO_VERSION = 4;
102
104
  const TASK_ROLE_SESSION_SET_FROM_VERSION = 4;
103
105
  const TASK_ROLE_SESSION_SET_TO_VERSION = 5;
104
106
  const STRUCTURED_PROVIDER_SESSION_SET_FROM_VERSION = 5;
105
107
  const STRUCTURED_PROVIDER_SESSION_SET_TO_VERSION = 6;
108
+ const AGENT_FIRST_SESSION_SET_FROM_VERSION = 6;
109
+ const AGENT_FIRST_SESSION_SET_TO_VERSION = 7;
106
110
  const PUBLICATION_REFERENCE_FROM_VERSION = 0;
107
111
  const PUBLICATION_REFERENCE_TO_VERSION = 1;
108
112
  const CONFIG_FROM_VERSION = 1;
@@ -203,12 +207,68 @@ export function createProductionStorageRegistry() {
203
207
  .registerOfflineMigration(capabilityGrantIntroductionStep())
204
208
  .registerOfflineMigration(releaseWorkflowIntroductionStep())
205
209
  .registerOfflineMigration(workMailboxV2Step())
210
+ .registerOfflineMigration(agentRunOwnedTaskRoleStep())
206
211
  .registerOfflineMigration(taskRoleSessionSetV5Step())
207
212
  .registerOfflineMigration(structuredProviderSessionSetV6Step())
213
+ .registerOfflineMigration(agentFirstSessionSetV7Step())
208
214
  .registerOfflineMigration(publicationReferenceIntroductionStep());
209
215
  assertRegistryCoversBaselineToCurrent(registry);
210
216
  return registry;
211
217
  }
218
+ /** Removes the independently writable Role runtime projection; AgentRun owns workflow activity. */
219
+ function agentRunOwnedTaskRoleStep() {
220
+ return {
221
+ axis: "record",
222
+ recordKind: "taskRole",
223
+ fromVersion: TASK_ROLE_FROM_VERSION,
224
+ toVersion: TASK_ROLE_TO_VERSION,
225
+ preconditions: requireTaskRoleV3Family,
226
+ transform: migrateTaskRoleV3ToV4,
227
+ declaredEffects: []
228
+ };
229
+ }
230
+ function requireTaskRoleV3Family(snapshot) {
231
+ const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
232
+ if (versions.taskRole !== TASK_ROLE_FROM_VERSION) {
233
+ throw new Error(`Record taskRole migration requires manifest version ${TASK_ROLE_FROM_VERSION}.`);
234
+ }
235
+ if (snapshot.state === null)
236
+ return;
237
+ const tasks = asObject(snapshot.state.tasks, "state tasks");
238
+ for (const [taskId, rawAggregate] of Object.entries(tasks)) {
239
+ const aggregate = asObject(rawAggregate, `Task aggregate ${taskId}`);
240
+ const roles = asObject(aggregate.roles, `Task Roles ${taskId}`);
241
+ for (const [roleName, rawRole] of Object.entries(roles)) {
242
+ const role = asObject(rawRole, `Task Role ${taskId}/${roleName}`);
243
+ if (role.schemaVersion !== TASK_ROLE_FROM_VERSION) {
244
+ throw new Error(`Task Role ${taskId}/${roleName} must use schemaVersion ${TASK_ROLE_FROM_VERSION}.`);
245
+ }
246
+ }
247
+ }
248
+ }
249
+ function migrateTaskRoleV3ToV4(snapshot) {
250
+ requireTaskRoleV3Family(snapshot);
251
+ const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
252
+ const schemaManifest = {
253
+ ...snapshot.schemaManifest,
254
+ recordVersions: { ...versions, taskRole: TASK_ROLE_TO_VERSION }
255
+ };
256
+ if (snapshot.state === null)
257
+ return { schemaManifest, state: null };
258
+ const tasks = asObject(snapshot.state.tasks, "state tasks");
259
+ const nextTasks = Object.fromEntries(Object.entries(tasks).map(([taskId, rawAggregate]) => {
260
+ const aggregate = asObject(rawAggregate, `Task aggregate ${taskId}`);
261
+ const roles = asObject(aggregate.roles, `Task Roles ${taskId}`);
262
+ const nextRoles = Object.fromEntries(Object.entries(roles).map(([roleName, rawRole]) => {
263
+ const role = asObject(rawRole, `Task Role ${taskId}/${roleName}`);
264
+ const { status: _status, ...retained } = role;
265
+ void _status;
266
+ return [roleName, { ...retained, schemaVersion: TASK_ROLE_TO_VERSION }];
267
+ }));
268
+ return [taskId, { ...aggregate, roles: nextRoles }];
269
+ }));
270
+ return { schemaManifest, state: { ...snapshot.state, tasks: nextTasks } };
271
+ }
212
272
  function configV2Step() {
213
273
  return {
214
274
  axis: "record",
@@ -2718,6 +2778,89 @@ function structuredProviderSessionSetV6Step() {
2718
2778
  declaredEffects: []
2719
2779
  };
2720
2780
  }
2781
+ /**
2782
+ * A Provider binding describes one reusable Conversation. Run correlation now
2783
+ * belongs to the concrete Provider Turn instead of making the Conversation a
2784
+ * second writable projection of the current AgentRun.
2785
+ */
2786
+ function agentFirstSessionSetV7Step() {
2787
+ return {
2788
+ axis: "record",
2789
+ recordKind: "taskRoleSessionSet",
2790
+ fromVersion: AGENT_FIRST_SESSION_SET_FROM_VERSION,
2791
+ toVersion: AGENT_FIRST_SESSION_SET_TO_VERSION,
2792
+ preconditions: requireTaskRoleSessionSetV6Family,
2793
+ transform: migrateTaskRoleSessionSetV6ToV7,
2794
+ declaredEffects: []
2795
+ };
2796
+ }
2797
+ function requireTaskRoleSessionSetV6Family(snapshot) {
2798
+ const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
2799
+ if (versions.taskRoleSessionSet !== AGENT_FIRST_SESSION_SET_FROM_VERSION) {
2800
+ throw new Error(`Record taskRoleSessionSet migration requires manifest version ${AGENT_FIRST_SESSION_SET_FROM_VERSION}.`);
2801
+ }
2802
+ if (snapshot.state === null)
2803
+ return;
2804
+ const tasks = asObject(snapshot.state.tasks, "state tasks");
2805
+ for (const [taskId, rawAggregate] of Object.entries(tasks)) {
2806
+ const aggregate = asObject(rawAggregate, `Task aggregate ${taskId}`);
2807
+ const sets = asObject(aggregate.roleSessionSets, `Task Role session sets ${taskId}`);
2808
+ for (const [roleName, rawSet] of Object.entries(sets)) {
2809
+ const set = asObject(rawSet, `Task Role session set ${taskId}/${roleName}`);
2810
+ if (set.schemaVersion !== AGENT_FIRST_SESSION_SET_FROM_VERSION) {
2811
+ throw new Error(`Task Role session set ${taskId}/${roleName} must use schemaVersion ${AGENT_FIRST_SESSION_SET_FROM_VERSION}.`);
2812
+ }
2813
+ if (set.providerBinding !== null) {
2814
+ const binding = asObject(set.providerBinding, `Provider Binding ${taskId}/${roleName}`);
2815
+ if (binding.schemaVersion !== 2 || typeof binding.runId !== "string") {
2816
+ throw new Error(`Provider Binding ${taskId}/${roleName} must use schemaVersion 2.`);
2817
+ }
2818
+ }
2819
+ }
2820
+ }
2821
+ }
2822
+ function migrateTaskRoleSessionSetV6ToV7(snapshot) {
2823
+ requireTaskRoleSessionSetV6Family(snapshot);
2824
+ const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
2825
+ const schemaManifest = {
2826
+ ...snapshot.schemaManifest,
2827
+ recordVersions: {
2828
+ ...versions,
2829
+ taskRoleSessionSet: AGENT_FIRST_SESSION_SET_TO_VERSION
2830
+ }
2831
+ };
2832
+ if (snapshot.state === null)
2833
+ return { schemaManifest, state: null };
2834
+ const tasks = asObject(snapshot.state.tasks, "state tasks");
2835
+ const nextTasks = {};
2836
+ for (const [taskId, rawAggregate] of Object.entries(tasks)) {
2837
+ const aggregate = asObject(rawAggregate, `Task aggregate ${taskId}`);
2838
+ const rawSets = asObject(aggregate.roleSessionSets, `Task Role session sets ${taskId}`);
2839
+ const nextSets = {};
2840
+ for (const [roleName, rawSet] of Object.entries(rawSets)) {
2841
+ const set = asObject(rawSet, `Task Role session set ${taskId}/${roleName}`);
2842
+ let providerBinding = null;
2843
+ if (set.providerBinding !== null) {
2844
+ const binding = asObject(set.providerBinding, `Provider Binding ${taskId}/${roleName}`);
2845
+ const { runId, ...conversation } = binding;
2846
+ providerBinding = {
2847
+ ...conversation,
2848
+ schemaVersion: 3,
2849
+ turn: binding.turn === null
2850
+ ? null
2851
+ : { ...asObject(binding.turn, `Provider Turn ${taskId}/${roleName}`), runId }
2852
+ };
2853
+ }
2854
+ nextSets[roleName] = {
2855
+ ...set,
2856
+ schemaVersion: AGENT_FIRST_SESSION_SET_TO_VERSION,
2857
+ providerBinding
2858
+ };
2859
+ }
2860
+ nextTasks[taskId] = { ...aggregate, roleSessionSets: nextSets };
2861
+ }
2862
+ return { schemaManifest, state: { ...snapshot.state, tasks: nextTasks } };
2863
+ }
2721
2864
  function requireTaskRoleSessionSetV5Family(snapshot) {
2722
2865
  const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
2723
2866
  if (versions.taskRoleSessionSet !== STRUCTURED_PROVIDER_SESSION_SET_FROM_VERSION) {
@@ -64,7 +64,7 @@ export const CURRENT_GLOBAL_ROLE_SESSION_SET_SCHEMA_VERSION = 3;
64
64
  export const CURRENT_TASK_SCHEMA_VERSION = 6;
65
65
  export const CURRENT_TASK_BRIEF_SCHEMA_VERSION = 2;
66
66
  export const CURRENT_CONTEXT_SNAPSHOT_SCHEMA_VERSION = 1;
67
- export const CURRENT_TASK_ROLE_SCHEMA_VERSION = 3;
67
+ export const CURRENT_TASK_ROLE_SCHEMA_VERSION = 4;
68
68
  export const CURRENT_MANAGED_WORKSPACE_SCHEMA_VERSION = 2;
69
69
  export const CURRENT_WORK_ITEM_SCHEMA_VERSION = 12;
70
70
  export const CURRENT_REVIEW_ROUND_SCHEMA_VERSION = 6;
@@ -118,7 +118,7 @@ export const CURRENT_STORED_TASK_SCHEMA_VERSION = 18;
118
118
  * Keep these named at the storage boundary so the upgrade record-axis map can
119
119
  * assert it is classifying the same bytes the store reads and writes.
120
120
  */
121
- export const CURRENT_TASK_ROLE_SESSION_SET_SCHEMA_VERSION = 6;
121
+ export const CURRENT_TASK_ROLE_SESSION_SET_SCHEMA_VERSION = 7;
122
122
  /**
123
123
  * v7 combines optional Issue 04 retry/receipt fields and Issue 05 Leader
124
124
  * actionability fields. All are optional, so the v6→v7 migration is a
@@ -92,6 +92,9 @@ export function buildWebTaskDetail(store, taskId, now = new Date()) {
92
92
  const effectiveLaunch = activeRun?.effective ?? activeSession?.effective ?? null;
93
93
  return {
94
94
  ...role,
95
+ // Presentation only: workflow activity is derived from AgentRun; the
96
+ // native Session contributes lifecycle detail when no Run is active.
97
+ status: activeRun === undefined ? activeSession?.status ?? "idle" : "running",
95
98
  sessionTokens: projectSessionTokenMetrics(events, resolveSessionTokenIdentity(activeSession === undefined
96
99
  ? null
97
100
  : { taskId, roleName: role.name, ...activeSession })),
@@ -2,9 +2,15 @@
2
2
 
3
3
  # Yui
4
4
 
5
- Yui 是面向持久 Codex/Claude 工作的本地控制平面。用户只需和 Operator
6
- 对话;Operator 将不同 Project 的需求、Bug、审查和问题咨询路由到对应
7
- Task,Leader 再负责拆解、执行选择、验收和安全集成。
5
+ Yui 是面向智能 Codex/Claude Agent 的本地控制平面。它持久保存用户意图、
6
+ Project Knowledge、Task、交接和结果,并提供上下文、消息、委派、工作区、
7
+ Session、审查和集成等小而原子的能力。Agent 组合这些能力,自主决定规划、
8
+ 顺序、委派、重试和恢复。
9
+
10
+ Yui 不把 Agent 的判断固化成确定性的工作流引擎。核心只负责持久身份、用户
11
+ 授权、工作区隔离和原子状态变更;Provider Session 与运行时观测用于执行和
12
+ 连续性,但不是 Task 事实的另一套来源。用户只需和 Operator 对话,Operator
13
+ 负责路由,Leader 负责目标拆解、执行选择、验收和集成。
8
14
 
9
15
  当前实现保留实用的 Role/Agent/session 与 CLI 框架,不恢复后期膨胀的数据维护、租约、定时调度和恢复账本体系。
10
16
 
@@ -510,9 +516,11 @@ Task 生命周期的交互选择只展示有效来源状态:activate 只展示
510
516
 
511
517
  ## Session 与 tmux
512
518
 
513
- 受管理的 Provider 会话仍然是普通用户会话。Yui 只添加对应的 Role Skill 与 Session Manifest 指针,并通过 Provider 原生结构化协议提交 Task 工作;Yui 不接管完整对话历史。受管理输入绝不会作为终端按键、粘贴文本或启动 argv 发送。Codex 由每个存活的 Role Runtime 启动并持有独立的 `app-server` 子进程;Task execution stop 会终止 Agent Host 及其 Provider 进程组,start 会创建新的受控运行时,不再依赖共享 Codex daemon。原生 thread 历史仍由 Codex 保留,Task、WorkItem、代码与持久消息仍由 Yui 保留。Claude 继续使用独立的持久 stream-json 进程,并以精确回放的 user message 作为接收确认。
519
+ 受管理的 Provider 会话仍然是普通用户会话。Yui 只添加对应的 Role Skill 与 Session Manifest 指针,并通过 Provider 原生结构化协议提交 Task 工作;Yui 不接管完整对话历史。受管理输入绝不会作为终端按键、粘贴文本或启动 argv 发送。Codex 在只转发字节的 `app-server proxy` 上完成 App Server WebSocket 握手,接入与 Desktop 相同的共享 daemon;原生 thread 可在 Desktop 中直接查看和操作。Task execution stop 只终止 Yui 的 Agent Host、WebSocket proxy,保留共享 daemon、原生 threadTask、WorkItem、代码与持久消息;start 创建新的 attachment。Claude 继续使用独立的持久 stream-json 进程,并以精确回放的 user message 作为接收确认。
520
+
521
+ Run、Conversation、Activation 与 Turn 是四个独立身份。Conversation 可以跨多个 Run 和客户端连接;Activation 只代表 Yui 当前的连接,而不是对 Provider thread 的独占所有权;Turn 在写入前先持久化。写入超时或结果不明确会进入 `delivery-unknown`,不会自动重发。Codex 已存在的原生 active Turn 只会让 Yui 暂时等待,不会导致 Yui Run 失败;Claude 等独立进程 Provider 继续通过 Yui 的 view/takeover 边界进行人工控制。
514
522
 
515
- Run、Conversation、Activation Turn 是四个独立身份。Conversation 可以跨多个 Run 和客户端连接;Activation 只代表 Yui 当前的连接,而不是对 Provider thread 的独占所有权;Turn 在写入前先持久化。写入超时或结果不明确会进入 `delivery-unknown`,不会自动重发。已经存在的原生 active Turn 只会让 Yui 暂时等待,不会导致 Yui Run 失败;存活的受管理 Role 应通过 Yui view/takeover 边界进行人工控制,避免其他客户端并发写入。
523
+ AgentRun Role 是否有工作正在执行的唯一持久调度状态。Conversation 不再维护另一份 current Run;Provider Turn 中的 Run id 只用于关联接收回执和终态事件。TaskRole 本身只保存身份和期望启动配置,不再保存可写的运行状态;CLI/Web 展示的 Role 状态由活动 AgentRun 派生,并叠加 Session/Driver 生命周期事实用于诊断。Agent 可以在 Provider 终态到达前声明旧 Run 的语义结果,下一批 mailbox 工作也可以先形成新的 AgentRun;Agent Host 仍会串行等待旧 Turn 结束,再投递被保留的新输入,不回滚事件,也不把旧 Conversation 强行换绑到新 Run。
516
524
 
517
525
  Task Role 使用以下显式入口:
518
526
 
@@ -524,7 +532,7 @@ yui task role takeover <task-id> <role>
524
532
  yui task role release <task-id> <role>
525
533
  ```
526
534
 
527
- Codex Role thread 仍可在 Desktop 中查看,但存活的受管理 Role 应通过 `view`、`takeover`、`release` 进行人工控制;若要从其他客户端恢复该 thread,应先停止 Task execution。Yui 不写入全局 Hook/config,也不依赖或控制共享 Codex daemonGlobal Operator 与 global Role 继续使用原生交互式 CLI,不属于受管理 Task Provider 协议。
535
+ Codex Role thread 可在 Desktop 中直接查看和操作;Desktop 已有 active Turn 时,Yui 只保留待投递工作并等待,不会失败或重复投递。`view`、`takeover`、`release` 继续作为 Claude 等独立进程 Provider 的人工控制入口。Yui 不写入全局 Hook/config,也不启动、重启或停止共享 daemon;Codex CLI/daemon 故障由 Task 生命周期之外修复。Global Operator 与 global Role 继续使用原生交互式 CLI,不属于受管理 Task Provider 协议。
528
536
 
529
537
  当新版本需要离线迁移 Home 时,应等待当前 Turn/Run 完成,然后从普通 shell
530
538
  执行 `yui session stop --all`,再重新执行 `yui update`。停止命令会先整体预检:
@@ -560,7 +568,7 @@ state、receipt 与 pane fence。Yui 不会解析 prompt glyph、进度文本、
560
568
  或其他 Agent 终端输出来推断 ready 或 success。`captureRole()` 只用于显式的人类
561
569
  transcript 查看,不具备生命周期权威。
562
570
 
563
- 稳定的 Role 上下文不会创建额外的 bootstrap Turn。Task execution Run 按角色使用通用 Leader 或 Worker Skill,review Run 则按持久 Run purpose 使用通用 Reviewer Skill;Provider 可以通过安全的追加式原生上下文通道携带 Skill,也可以在普通 Task 投递中指向它。这些都只是 Yui 自己拥有的可移植编排规则。Project Skills 始终是 Project 中正常版本化的文件,由 Agent 通过自身项目机制发现、选择并按需加载;Yui 不扫描、不解析、不复制,也不注入 Project Skills。Managed Codex 保留用户原有的 developer instructions;普通 Task 消息会携带精简的 Session Manifest 绝对路径,Manifest 再指向对应的 Yui Role Skill,供 Codex 按需读取。每个存活 Role 的 Yui-owned App Server 进程会加载所选 Codex 原生 config profile;model、effort、permission、workspace 与 shell 设置作为线程级 `thread/start` 或 `thread/resume` 配置传入,Yui 不修改底层 Codex 配置文件。App Server 原生通知是 Managed Codex 线程的生命周期权威;Yui 不为它安装 Hook,也不占用 `notify`。交互式 Codex Session 仍可使用 Yui 的结构化 `notify` callback,Doctor 会报告最终生效的配置冲突。`skills.config` 只负责启停已发现 Skill,Yui 不会误用它。Claude 从 Yui 管理的私有 `0600` context 文件读取同一份 Yui Role Skill 内容,不再把大段或敏感文本放进 argv;重试和 resume 会复用按 purpose 区分的稳定路径。非 Operator 的 global Role 保持中性,不会注入 Task 编排 Skill。因此 Operator 会停在空白的原生 composer,用户输入仍是第一条 user message;Leader wake、Worker 和 Reviewer Run assignment 仍是邮箱投递的真实工作消息。
571
+ 稳定的 Role 上下文不会创建额外的 bootstrap Turn。Task execution Run 按角色使用通用 Leader 或 Worker Skill,review Run 则按持久 Run purpose 使用通用 Reviewer Skill;Provider 可以通过安全的追加式原生上下文通道携带 Skill,也可以在普通 Task 投递中指向它。这些都只是 Yui 自己拥有的可移植编排规则。Project Skills 始终是 Project 中正常版本化的文件,由 Agent 通过自身项目机制发现、选择并按需加载;Yui 不扫描、不解析、不复制,也不注入 Project Skills。Managed Codex 保留用户原有的 developer instructions;普通 Task 消息会携带精简的 Session Manifest 绝对路径,Manifest 再指向对应的 Yui Role Skill,供 Codex 按需读取。model、effort、permission、workspace 与 shell 设置作为共享 daemon 上的线程级 `thread/start` 或 `thread/resume` 配置传入;Codex 原生 config profile 因无法隔离到单条共享 thread 而被拒绝,Yui 不修改底层 Codex 配置文件。App Server 原生通知是 Managed Codex 线程的生命周期权威;Yui 不为它安装 Hook,也不占用 `notify`。交互式 Codex Session 仍可使用 Yui 的结构化 `notify` callback,Doctor 会报告最终生效的配置冲突。`skills.config` 只负责启停已发现 Skill,Yui 不会误用它。Claude 从 Yui 管理的私有 `0600` context 文件读取同一份 Yui Role Skill 内容,不再把大段或敏感文本放进 argv;重试和 resume 会复用按 purpose 区分的稳定路径。非 Operator 的 global Role 保持中性,不会注入 Task 编排 Skill。因此 Operator 会停在空白的原生 composer,用户输入仍是第一条 user message;Leader wake、Worker 和 Reviewer Run assignment 仍是邮箱投递的真实工作消息。
564
572
 
565
573
  ## Controller 与失败处理
566
574
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zq-silk/yui",
3
- "version": "0.13.6",
3
+ "version": "0.13.8",
4
4
  "description": "Local control plane for long-running native agent CLI sessions backed by tmux.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -12,10 +12,11 @@ workspace layout, native transcript, or an earlier Run.
12
12
  For every managed Task Run:
13
13
 
14
14
  1. Read the exact Run identity from the newest Bootstrap Envelope.
15
- 2. Before acting, load its authorized pack with the ordinary Yui CLI command:
15
+ 2. Before acting, load its authorized pack with the Session CLI named by the
16
+ current Session Manifest:
16
17
 
17
18
  ```sh
18
- yui task run context "$YUI_TASK_ID/<run-id>" --json
19
+ "$YUI_SESSION_CLI" task run context "$YUI_TASK_ID/<run-id>" --json
19
20
  ```
20
21
 
21
22
  3. Verify that the returned Task, Run, Role, purpose, Snapshot digest, workspace,
@@ -27,7 +28,7 @@ For every managed Task Run:
27
28
  `refId`:
28
29
 
29
30
  ```sh
30
- yui task run context expand "$YUI_TASK_ID/<run-id>" <ref-id> --store <store> --mode full --json
31
+ "$YUI_SESSION_CLI" task run context expand "$YUI_TASK_ID/<run-id>" <ref-id> --store <store> --mode full --json
31
32
  ```
32
33
 
33
34
  A bare `<ref-id>` remains supported only when it identifies exactly one
@@ -46,7 +47,7 @@ For a global Operator or custom GlobalRole Session, load the stable authorized v
46
47
  before routing or acting:
47
48
 
48
49
  ```sh
49
- yui session context "$YUI_ROLE" --json
50
+ "$YUI_SESSION_CLI" session context "$YUI_ROLE" --json
50
51
  ```
51
52
 
52
53
  Global context grants no Task implementation workspace. Read a Task only after