@zq-silk/yui 0.7.1 → 0.8.1

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 (47) hide show
  1. package/README.md +42 -23
  2. package/dist/cli/commandCatalog.js +234 -122
  3. package/dist/cli/completion.js +3 -3
  4. package/dist/cli/helpRenderer.js +3 -0
  5. package/dist/cli/interactionPolicy.js +44 -23
  6. package/dist/cli/interactiveSelection.js +1 -1
  7. package/dist/cli/invocationRouter.js +1 -1
  8. package/dist/cli/roleWizard.js +8 -8
  9. package/dist/cli.js +116 -72
  10. package/dist/commands/agentCommands.js +5 -5
  11. package/dist/commands/configCommands.js +351 -104
  12. package/dist/commands/configOverview.js +60 -0
  13. package/dist/commands/deliveryGuardPreflight.js +2 -2
  14. package/dist/commands/globalRoleCommands.js +9 -9
  15. package/dist/commands/profileCommands.js +8 -8
  16. package/dist/commands/resourcesCommands.js +6 -5
  17. package/dist/commands/taskCommands.js +3 -6
  18. package/dist/commands/taskRoleRuntimeStatus.js +3 -1
  19. package/dist/commands/telemetryCommands.js +11 -6
  20. package/dist/config/configCatalog.js +42 -0
  21. package/dist/config/yuiConfig.js +80 -35
  22. package/dist/context/sessionBootstrapManifest.js +1 -1
  23. package/dist/controller/clientRuntime.js +0 -2
  24. package/dist/controller/controller.js +21 -9
  25. package/dist/controller/fileSchedulerStoreAdapter.js +20 -9
  26. package/dist/controller/runtime.js +32 -18
  27. package/dist/doctor/doctor.js +2 -2
  28. package/dist/resources/autoResourceGc.js +3 -1
  29. package/dist/review/reviewConfig.js +0 -2
  30. package/dist/run/providerRetry.js +29 -16
  31. package/dist/run/providerRetryConfig.js +5 -3
  32. package/dist/runtime/launchDiagnostics.js +1 -1
  33. package/dist/scheduler/roleRunStall.js +12 -9
  34. package/dist/setup/setupCommand.js +153 -492
  35. package/dist/storage/compatibleTaskStore.js +9 -5
  36. package/dist/storage/migration/productionRegistry.js +58 -0
  37. package/dist/storage/taskStore.js +21 -2
  38. package/dist/telemetry/sqliteTelemetryStore.js +9 -1
  39. package/dist/telemetry/telemetryConfig.js +1 -18
  40. package/dist/telemetry/telemetryStore.js +2 -2
  41. package/dist/telemetry/telemetryWiring.js +6 -5
  42. package/dist/web/webSnapshot.js +5 -3
  43. package/i18n/README.zh-CN.md +37 -32
  44. package/package.json +1 -1
  45. package/skills/yui-leader/SKILL.md +12 -5
  46. package/skills/yui-operator/SKILL.md +44 -6
  47. package/skills/yui-runtime/SKILL.md +1 -1
@@ -6,19 +6,23 @@ import { FileTaskStore, STORAGE_STATE_FILE, stateFileFingerprint, StorageRecordE
6
6
  import { readSqliteHomeIdentity, SqliteTaskStore } from "./sqliteStore.js";
7
7
  import { COMMITTED_DATABASE_FILENAME } from "./upgrade/sqliteStateMigration.js";
8
8
  import { validateHomeIdentity } from "../repository/homeIdentity.js";
9
- import { ensureStorageSchema, inspectStorageSchema, readStorageSchemaManifest, STORAGE_SCHEMA_FILE } from "./storageSchema.js";
9
+ import { CURRENT_STORAGE_LAYOUT_VERSION, ensureStorageSchema, inspectStorageSchema, readStorageSchemaManifest, STORAGE_SCHEMA_FILE } from "./storageSchema.js";
10
10
  import { classifyHome } from "./upgrade/homeClassification.js";
11
11
  import { inspectSnapshotVersionState } from "./upgrade/homeMigrationTarget.js";
12
12
  import { latestStorageVersionState } from "./upgrade/recordVersions.js";
13
13
  export { createProductionStorageRegistry } from "./migration/productionRegistry.js";
14
14
  /**
15
- * Initialize a brand-new Home, or open an existing Home through the same
16
- * compatibility classification as every ordinary command. Setup is the one
17
- * ordinary flow that is also responsible for creating the initial manifest.
15
+ * Initialize a brand-new Home with the current authoritative backend, or open
16
+ * an existing Home through the same compatibility classification as every
17
+ * ordinary command. Setup is the one ordinary flow that is also responsible
18
+ * for creating the initial manifest.
18
19
  */
19
- export function initializeCompatibleFileTaskStore(home, options = {}) {
20
+ export function initializeCompatibleTaskStore(home, options = {}) {
20
21
  if (inspectStorageSchema(home).status === "uninitialized") {
21
22
  ensureStorageSchema(home);
23
+ if (CURRENT_STORAGE_LAYOUT_VERSION >= 7) {
24
+ return new SqliteTaskStore(home);
25
+ }
22
26
  }
23
27
  return openCompatibleFileTaskStore(home, options);
24
28
  }
@@ -81,6 +81,8 @@ const TASK_ROLE_SESSION_SET_FROM_VERSION = 4;
81
81
  const TASK_ROLE_SESSION_SET_TO_VERSION = 5;
82
82
  const PUBLICATION_REFERENCE_FROM_VERSION = 0;
83
83
  const PUBLICATION_REFERENCE_TO_VERSION = 1;
84
+ const CONFIG_FROM_VERSION = 1;
85
+ const CONFIG_TO_VERSION = 2;
84
86
  /**
85
87
  * Build the authoritative production graph. Transition intent and executable
86
88
  * transforms are registered together here; compatible loading and offline
@@ -129,6 +131,7 @@ export function createProductionStorageRegistry() {
129
131
  declaredEffects: []
130
132
  })
131
133
  .registerOfflineMigration(projectOwnershipStep())
134
+ .registerOfflineMigration(configV2Step())
132
135
  .registerCompatible(projectKnowledgeProposalsStep())
133
136
  .registerOfflineMigration(taskWorkspaceIdentityStep())
134
137
  .registerOfflineMigration(recordFamilyStep("workItem", WORK_ITEM_FROM_VERSION, WORK_ITEM_TO_VERSION, "workItems"))
@@ -162,6 +165,61 @@ export function createProductionStorageRegistry() {
162
165
  assertRegistryCoversBaselineToCurrent(registry);
163
166
  return registry;
164
167
  }
168
+ function configV2Step() {
169
+ return {
170
+ axis: "record",
171
+ recordKind: "config",
172
+ fromVersion: CONFIG_FROM_VERSION,
173
+ toVersion: CONFIG_TO_VERSION,
174
+ preconditions: requireConfigV1,
175
+ transform: migrateConfigV1ToV2,
176
+ declaredEffects: []
177
+ };
178
+ }
179
+ function requireConfigV1(snapshot) {
180
+ const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
181
+ if (versions.config !== CONFIG_FROM_VERSION) {
182
+ throw new Error(`Record config migration requires manifest version ${CONFIG_FROM_VERSION}.`);
183
+ }
184
+ if (snapshot.state === null)
185
+ return;
186
+ const config = asObject(snapshot.state.config, "Yui config");
187
+ if (config.schemaVersion !== CONFIG_FROM_VERSION) {
188
+ throw new Error(`Yui config must use schemaVersion ${CONFIG_FROM_VERSION} before migration.`);
189
+ }
190
+ }
191
+ function migrateConfigV1ToV2(snapshot) {
192
+ requireConfigV1(snapshot);
193
+ const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
194
+ const schemaManifest = {
195
+ ...snapshot.schemaManifest,
196
+ recordVersions: { ...versions, config: CONFIG_TO_VERSION }
197
+ };
198
+ if (snapshot.state === null)
199
+ return { schemaManifest, state: null };
200
+ const config = asObject(snapshot.state.config, "Yui config");
201
+ const { providerRetryMaxWindowMs, yieldReceiptReplay: _yieldReceiptReplay, gitBin: _gitBin, telemetryMode, schemaVersion: _schemaVersion, ...retained } = config;
202
+ const providerRetryMaxWindowSeconds = typeof providerRetryMaxWindowMs === "number"
203
+ ? Math.ceil(providerRetryMaxWindowMs / 1_000)
204
+ : undefined;
205
+ const telemetryEnabled = telemetryMode === undefined
206
+ ? undefined
207
+ : telemetryMode === "dual" || telemetryMode === "bounded";
208
+ return {
209
+ schemaManifest,
210
+ state: {
211
+ ...snapshot.state,
212
+ config: {
213
+ ...retained,
214
+ schemaVersion: CONFIG_TO_VERSION,
215
+ ...(providerRetryMaxWindowSeconds === undefined
216
+ ? {}
217
+ : { providerRetryMaxWindowSeconds }),
218
+ ...(telemetryEnabled === undefined ? {} : { telemetryEnabled })
219
+ }
220
+ }
221
+ };
222
+ }
165
223
  function workItemExecutionGroupHistoryStep() {
166
224
  return {
167
225
  axis: "record",
@@ -6,7 +6,7 @@ import { validateConfiguredAgent } from "../agent/agent.js";
6
6
  import { validateCapabilityGrant } from "../grant/capabilityGrant.js";
7
7
  import { validateReleaseWorkflow } from "../release/releaseWorkflow.js";
8
8
  import { publicationExternalKey, validatePublicationReference } from "../task/publicationReference.js";
9
- import { reconciliationIntervalMilliseconds, resolveLeaderNextActionMode, resolveResourcesGcAutoQuarantine, resolveResourcesGcMode } from "../config/yuiConfig.js";
9
+ import { reconciliationIntervalMilliseconds, resolveAgentLaunchInactivityTimeoutSeconds, resolveControllerTaskConcurrency, resolveContextBudget, resolveDeliveryTimeoutSeconds, resolveLeaderNextActionMode, resolveLeaderSemanticBudgetTurns, resolveProviderRetryAdapters, resolveProviderRetryDelaysSeconds, resolveProviderRetryMaxWindowSeconds, resolveProviderRetryMode, resolveResourcesGcAutoQuarantine, resolveResourcesGcMode, resolveResourcesQuarantineTtlHours, resolveRuntimeHealth, resolveTelemetryEnabled, resolveTelemetryRunCap, resolveTelemetryTerminalKeep, resolveTmuxBin, resolveTmuxHistoryLimit } from "../config/yuiConfig.js";
10
10
  import { resolveTimeZone } from "../output/timePresentation.js";
11
11
  import { mailboxBatches, consumePendingBatch, mailboxHasWork, mailboxTargetKey, pendingLane, validateWorkMailbox } from "../coordination/workMailbox.js";
12
12
  import { validateContextSnapshot } from "../context/contextSnapshot.js";
@@ -44,7 +44,7 @@ import { CURRENT_AGGREGATE_SCHEMA_VERSION, requireCompatibleStorageSchema, requi
44
44
  export const STORAGE_STATE_FILE = "state.json";
45
45
  /** The root StorageState schema is the persisted aggregate document version. */
46
46
  export const CURRENT_STORAGE_STATE_SCHEMA_VERSION = CURRENT_AGGREGATE_SCHEMA_VERSION;
47
- export const CURRENT_CONFIG_SCHEMA_VERSION = 1;
47
+ export const CURRENT_CONFIG_SCHEMA_VERSION = 2;
48
48
  export const CURRENT_HOME_IDENTITY_SCHEMA_VERSION = 1;
49
49
  export const CURRENT_ACTIVE_RUN_POINTER_SCHEMA_VERSION = 3;
50
50
  /**
@@ -2449,13 +2449,32 @@ function observeTaskRecordId(aggregate, kind, id) {
2449
2449
  }
2450
2450
  export function validateYuiConfig(config) {
2451
2451
  try {
2452
+ if (config.schemaVersion !== CURRENT_CONFIG_SCHEMA_VERSION) {
2453
+ throw new TypeError(`Yui config must use schemaVersion ${CURRENT_CONFIG_SCHEMA_VERSION}.`);
2454
+ }
2452
2455
  reconciliationIntervalMilliseconds(config.reconciliationIntervalSeconds);
2453
2456
  resolveTimeZone(config.timeZone);
2454
2457
  if (config.review !== undefined)
2455
2458
  validateReviewConfig(config.review);
2456
2459
  resolveLeaderNextActionMode(config.leaderNextActionMode);
2460
+ resolveContextBudget(config.contextBudget);
2457
2461
  resolveResourcesGcMode(config.resourcesGcMode);
2458
2462
  resolveResourcesGcAutoQuarantine(config.resourcesGcAutoQuarantine);
2463
+ resolveResourcesQuarantineTtlHours(config.resourcesQuarantineTtlHours);
2464
+ resolveProviderRetryMode(config.providerRetryMode);
2465
+ resolveProviderRetryAdapters(config.providerRetryAdapters);
2466
+ resolveProviderRetryDelaysSeconds(config.providerRetryDelaysSeconds);
2467
+ resolveProviderRetryMaxWindowSeconds(config.providerRetryMaxWindowSeconds);
2468
+ resolveRuntimeHealth(config.runtimeHealth);
2469
+ resolveControllerTaskConcurrency(config.controllerTaskConcurrency);
2470
+ resolveAgentLaunchInactivityTimeoutSeconds(config.agentLaunchInactivityTimeoutSeconds);
2471
+ resolveDeliveryTimeoutSeconds(config.deliveryTimeoutSeconds);
2472
+ resolveLeaderSemanticBudgetTurns(config.leaderSemanticBudgetTurns);
2473
+ resolveTmuxBin(config.tmuxBin);
2474
+ resolveTmuxHistoryLimit(config.tmuxHistoryLimit);
2475
+ resolveTelemetryEnabled(config.telemetryEnabled);
2476
+ resolveTelemetryTerminalKeep(config.telemetryTerminalKeep);
2477
+ resolveTelemetryRunCap(config.telemetryRunCap);
2459
2478
  }
2460
2479
  catch (error) {
2461
2480
  throw new StorageRecordError(error instanceof Error ? error.message : "Yui reconciliation interval is invalid.");
@@ -38,7 +38,7 @@ export class SqliteTelemetryStore {
38
38
  #flushScheduled = false;
39
39
  #closed = false;
40
40
  constructor(home, options = {}) {
41
- this.mode = options.mode ?? "dual";
41
+ this.mode = options.mode ?? "on";
42
42
  this.#path = join(home, COMMITTED_DATABASE_FILENAME);
43
43
  this.#terminalKeep = options.terminalKeep ?? DEFAULT_TERMINAL_KEEP;
44
44
  this.#runCap = options.runCap ?? DEFAULT_RUN_CAP;
@@ -286,9 +286,17 @@ export class SqliteTelemetryStore {
286
286
  WHERE COALESCE(excluded.sequence, -1) > COALESCE(telemetry.sequence, -1)
287
287
  OR (excluded.sequence IS telemetry.sequence AND excluded.received_at >= telemetry.received_at)`);
288
288
  try {
289
+ const touchedRuns = new Map();
289
290
  db.transaction(() => {
290
291
  for (const entry of batch.values()) {
291
292
  upsert.run(entry.taskId, entry.roleName, entry.runId, entry.generation, entry.progressId, entry.sequence ?? null, JSON.stringify(entry.payload), entry.receivedAt);
293
+ touchedRuns.set(`${entry.taskId}\0${entry.runId}`, {
294
+ taskId: entry.taskId,
295
+ runId: entry.runId
296
+ });
297
+ }
298
+ for (const { taskId, runId } of touchedRuns.values()) {
299
+ this.capRun(taskId, runId);
292
300
  }
293
301
  })();
294
302
  this.#applied += batch.size;
@@ -7,7 +7,7 @@
7
7
  * the schema's own constants (§4.4); the environment only overrides them.
8
8
  */
9
9
  import { TELEMETRY_KEEP_PER_GENERATION, TELEMETRY_RUN_CAP } from "../storage/sqliteSchema.js";
10
- export const DEFAULT_TELEMETRY_MODE = "legacy";
10
+ export const DEFAULT_TELEMETRY_MODE = "off";
11
11
  /** Terminal Run/generation progress rows retained after prune. */
12
12
  export const DEFAULT_TERMINAL_KEEP = TELEMETRY_KEEP_PER_GENERATION;
13
13
  /** Hard cap of progress rows per Run while it is still active. */
@@ -18,23 +18,6 @@ export const DEFAULT_RUN_CAP = TELEMETRY_RUN_CAP;
18
18
  * rows (Tasks × Runs × cap).
19
19
  */
20
20
  export const MAX_RUN_CAP = 10_000_000;
21
- const TELEMETRY_MODES = ["legacy", "dual", "bounded"];
22
- /**
23
- * Resolve the telemetry mode from the durable config value (default `legacy`).
24
- * Only the three exact values (case-insensitive) are accepted; anything else
25
- * fails closed at startup instead of silently changing diagnostic retention.
26
- */
27
- export function resolveTelemetryMode(value) {
28
- if (typeof value !== "string")
29
- return DEFAULT_TELEMETRY_MODE;
30
- const raw = value.trim().toLowerCase();
31
- if (raw === undefined || raw === "")
32
- return DEFAULT_TELEMETRY_MODE;
33
- if (!TELEMETRY_MODES.includes(raw)) {
34
- throw new TypeError(`telemetryMode must be one of ${TELEMETRY_MODES.join(", ")}; got ${JSON.stringify(raw)}.`);
35
- }
36
- return raw;
37
- }
38
21
  /**
39
22
  * Resolve the terminal-Run retention window from the durable config value
40
23
  * (default 200). Must be a positive integer.
@@ -1,7 +1,7 @@
1
- /** No-op sink for `legacy` mode and for callers without a sidecar. */
1
+ /** No-op sink for disabled telemetry and callers without a sidecar. */
2
2
  export class NullTelemetrySink {
3
3
  mode;
4
- constructor(mode = "legacy") {
4
+ constructor(mode = "off") {
5
5
  this.mode = mode;
6
6
  }
7
7
  observe(_entry) { }
@@ -1,6 +1,7 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { resolveRunCap, resolveTelemetryMode, resolveTerminalKeep } from "./telemetryConfig.js";
3
+ import { resolveRunCap, resolveTerminalKeep } from "./telemetryConfig.js";
4
+ import { resolveTelemetryEnabled } from "../config/yuiConfig.js";
4
5
  import { SqliteTelemetryStore } from "./sqliteTelemetryStore.js";
5
6
  import { COMMITTED_DATABASE_FILENAME } from "../storage/upgrade/sqliteStateMigration.js";
6
7
  /**
@@ -16,12 +17,12 @@ import { COMMITTED_DATABASE_FILENAME } from "../storage/upgrade/sqliteStateMigra
16
17
  * and never blocks the semantic lane.
17
18
  */
18
19
  export function openSchedulerTelemetry(home, config) {
19
- const mode = resolveTelemetryMode(config.telemetryMode);
20
- if (mode === "legacy")
20
+ if (!resolveTelemetryEnabled(config.telemetryEnabled))
21
21
  return null;
22
+ const mode = "on";
22
23
  const dbPath = join(home, COMMITTED_DATABASE_FILENAME);
23
24
  if (!existsSync(dbPath)) {
24
- throw new Error(`telemetryMode=${mode} requires SQLite storage, but ${dbPath} does not exist. `
25
+ throw new Error(`telemetryEnabled=true requires SQLite storage, but ${dbPath} does not exist. `
25
26
  + "Migrate this Home to the database backend first (yui upgrade).");
26
27
  }
27
28
  const store = new SqliteTelemetryStore(home, {
@@ -29,5 +30,5 @@ export function openSchedulerTelemetry(home, config) {
29
30
  terminalKeep: resolveTerminalKeep(config.telemetryTerminalKeep),
30
31
  runCap: resolveRunCap(config.telemetryRunCap)
31
32
  });
32
- return { mode, sink: store, reader: store };
33
+ return { mode, sink: store, reader: store, retention: store };
33
34
  }
@@ -6,6 +6,7 @@ import { projectRunRecovery, readRunRecoveryFacts } from "../run/recoveryProject
6
6
  import { classifyRuntimeHealth, projectRuntimeTaskEvents } from "../runtime/runtimeProjection.js";
7
7
  import { builtinDriverIdForAdapter } from "../runtime/builtinAgentDrivers.js";
8
8
  import { formatAgentRunReceiptId } from "../task/taskRecordReference.js";
9
+ import { resolveRuntimeHealth } from "../config/yuiConfig.js";
9
10
  export function buildWebDashboardSnapshot(store, now = new Date()) {
10
11
  return store.transaction((reader) => {
11
12
  const statusCounts = {
@@ -88,7 +89,7 @@ export function buildWebTaskDetail(store, taskId, now = new Date()) {
88
89
  .map((run) => [run.roleName, run]));
89
90
  const activeRunHealth = runs
90
91
  .filter((run) => run.status === "active")
91
- .map((run) => projectWebRunRuntimeHealth(reader, taskId, run, events, now));
92
+ .map((run) => projectWebRunRuntimeHealth(reader, taskId, run, events, now, resolveRuntimeHealth(reader.getConfig().runtimeHealth)));
92
93
  const roles = reader.listRoles(taskId).map((role) => {
93
94
  const activeRun = activeRuns.get(role.name);
94
95
  const sessions = reader.getTaskRoleSessionSet(taskId, role.name);
@@ -137,7 +138,7 @@ function latestStallProgress(events, runId) {
137
138
  * scheduler's durable `run.stalled` episode is surfaced as
138
139
  * `stalled-candidate`.
139
140
  */
140
- function projectWebRunRuntimeHealth(reader, taskId, run, events, now) {
141
+ function projectWebRunRuntimeHealth(reader, taskId, run, events, now, policy) {
141
142
  const stalled = isRoleRunStalled(events, run.id);
142
143
  const sessions = reader.getTaskRoleSessionSet(taskId, run.roleName);
143
144
  const session = sessions?.sessions[run.effective.agentId];
@@ -193,7 +194,8 @@ function projectWebRunRuntimeHealth(reader, taskId, run, events, now) {
193
194
  const classification = classifyRuntimeHealth({
194
195
  projection,
195
196
  semanticProgressAt: semanticProgress.progressAt,
196
- now
197
+ now,
198
+ policy
197
199
  });
198
200
  return {
199
201
  runId: run.id,
@@ -45,31 +45,34 @@ yui setup
45
45
  yui doctor
46
46
  ```
47
47
 
48
- `setup` 是交互式的:检测已安装的 Agent CLI,选择要配置的 Agent、默认
49
- Agent Operator Agent,并实时探测所选 CLI 当前支持的模型。它先配置
50
- Leader Operator,再说明全局 Worker 配置会复制到新建的 Task Role
51
- 让用户选择 Worker 复用 Leader 配置还是单独配置。模型选择后只展示该模型
52
- 支持的思考强度。随后 setup 会确认位于 Yui home 外部的 Project workspace,
53
- 并询问 shell completion。选择器同时提供原生 CLI 默认值和自定义值入口。
54
- 再次运行不会删除已有 Task/Role,也不会改变当前安装的 Project workspace,
55
- 可用于安全地调整配置。setup 成功返回前会确保当前 Home 的后台 Controller
56
- 已经启动。
57
-
58
- 模型与思考强度属于 Agent binding 设置,因此 Operator、Leader 和全局
59
- Worker 即使使用同一个 Agent CLI,也可以采用不同配置。Profile 中的
60
- model/effort 只是 native child 的可移植 hint。
61
-
62
- Setup 会为每个受管 Agent binding 显式设置 `bypass` permission strategy。
63
- 后续 Role 更新可选择 `default`、`bypass` `configured`;`configured`
64
- 保留对应 adapter 的原生权限枚举与工具规则。
65
-
66
- 运行时能力目录会在每次命令中刷新,并缓存在 Yui home。实时探测超时或失败时,Yui 会展示同一 Agent 启动上下文最近一次成功的缓存并明确提示数据可能过期;没有匹配缓存时,则提供 CLI 默认值和自定义入口。`yui agent capabilities <id>` 可一次性读取同一份目录,包括模型、逐模型思考强度,以及权限、搜索可用性、profile、settings source、service tier 等其他运行时选项。
48
+ `setup` 被刻意缩减为最小流程:检查 tmux,复用或创建一个可用 Agent,在
49
+ Yui home 外创建默认 workspace,并配置 Operator Leader,使用户可以启动
50
+ Yui 并执行 Task。它不会创建 Worker、Reviewer、Profile review policy
51
+ 也不会询问 model/effort、permission shell completion。Operator 与 Leader
52
+ 的必需 binding 使用 Yui adapter 默认 permission strategy(`bypass`);
53
+ 后续调整统一通过 `config role` 完成。再次运行会原样保留已经可用的 Operator
54
+ Leader;setup 成功返回前会启动当前 Home 的后台 Controller。
55
+
56
+ 所有持久配置都位于 `yui config` 下。`config show` 展示完整有效状态,
57
+ `config --help` 介绍各配置域并给出示例。Operator 可通过结构化的
58
+ `config describe` 读取配置目录,向用户说明当前值、具体影响、可选值和生效
59
+ 方式,并只执行用户确认的修改。
60
+
61
+ 持久设置按职责分组:`config system` 管理 Home 默认值和展示方式,
62
+ `config runtime` 管理 Controller 健康阈值、并发、启动、投递和 Provider
63
+ 重试,`config workflow` 管理 Leader、context review policy,
64
+ `config resources` 管理隔离区和 GC,`config tools` 管理 tmux 与诊断
65
+ telemetry。Agent、全局 Role、Profile 和 shell completion 则继续位于同级的
66
+ `config agent|role|profile|completion` 域。每个持久设置域统一使用
67
+ `show`、`set`、`clear`。
68
+
69
+ 运行时能力目录会在每次命令中刷新,并缓存在 Yui home。实时探测超时或失败时,Yui 会展示同一 Agent 启动上下文最近一次成功的缓存并明确提示数据可能过期;没有匹配缓存时,则提供 CLI 默认值和自定义入口。`yui config agent capabilities <id>` 可一次性读取同一份目录,包括模型、逐模型思考强度,以及权限、搜索可用性、profile、settings source、service tier 等其他运行时选项。
67
70
 
68
71
  `completion` 无论是否指定 shell,都会进入确认流程:
69
72
 
70
73
  ```sh
71
- yui completion
72
- yui completion zsh
74
+ yui config completion
75
+ yui config completion zsh
73
76
  ```
74
77
 
75
78
  流程会确认生成脚本、安装路径和 shell 启动文件修改。补全脚本直接由命令目录生成,支持二级及更深层子命令。
@@ -116,16 +119,16 @@ yui task activate <task-id>
116
119
 
117
120
  ```sh
118
121
  yui config show
119
- yui config set time-zone Europe/London
122
+ yui config system set time-zone Europe/London
120
123
  ```
121
124
 
122
125
  WorkItem 审查只有一条可选的全局规则,并直接复用已有 Global Role 的
123
126
  Agent、model、权限、prompt 和 Skills:
124
127
 
125
128
  ```sh
126
- yui config set review --role reviewer --trigger always
129
+ yui config workflow set review --role reviewer --trigger always
127
130
  yui config show
128
- yui config clear review
131
+ yui config workflow clear review
129
132
  ```
130
133
 
131
134
  对带 Project 的软件交付,可使用 `--trigger final`:WorkItem 验收与
@@ -133,7 +136,7 @@ Integration 保持独立,在 Task 完成前只对所有已集成 Project 的
133
136
  一次 Task 级 ReviewRound:
134
137
 
135
138
  ```sh
136
- yui config set review --role reviewer --trigger final
139
+ yui config workflow set review --role reviewer --trigger final
137
140
  ```
138
141
 
139
142
  每个进入 Leader 验收阶段的结果,都会成为原 WorkItem 上一个明确的候选。
@@ -261,7 +264,7 @@ Task/WorkItem 模型,不增加额外任务类型。
261
264
  从已配置的全局 Worker 创建 Task Role,应用 Profile 并派发 WorkItem:
262
265
 
263
266
  ```sh
264
- yui role show worker
267
+ yui config role show worker
265
268
  yui task role add <task-id> implementer --profile implementer
266
269
  yui task role show <task-id> implementer
267
270
 
@@ -327,7 +330,7 @@ yui task work create <task-id> "审查实现" \
327
330
  --objective "返回有源码依据的问题" \
328
331
  --accept "每个问题都标明受影响路径"
329
332
  yui task work update <task-id>/<work-item-id> running
330
- yui profile show reviewer
333
+ yui config profile show reviewer
331
334
  ```
332
335
 
333
336
  subagent 的创建与结果返回完全由 Leader 当前 Agent 的 native child 能力
@@ -441,7 +444,7 @@ tmux 会在 pane 创建时固定其历史容量。配置该限制之前创建的
441
444
  Global 交互入口在不存在 writer 时保持可写;已有 writer 时自动降级为只读。global Web 对每个 tmux session 只允许一个 writer;Task Web 始终只读。Task CLI 入口除非显式请求 `--read-write`,否则始终只读,避免观察动作改变 Agent 执行。
442
445
 
443
446
  ```sh
444
- yui role enter <global-role>
447
+ yui session enter <global-role>
445
448
  yui task enter <task-id> [role] [--read-only | --read-write]
446
449
  yui task role enter <task-id> <role> [--read-only | --read-write]
447
450
  ```
@@ -455,7 +458,7 @@ binding 是预先保存、可随时切换的配置,而不是并行身份。Ope
455
458
  并切换。跨 Agent 切换默认复用已保存的 model/effort,只有用户明确选择
456
459
  更新时才进入现有配置选择流程。
457
460
 
458
- 使用 `yui role unbind <global-role> <agent-id>` 或 `yui task role unbind <task-id> <role> <agent-id>` 可移除休眠 binding。active binding 或任何未 stopped 的 native session 都会被拒绝;stopped session 记录会和 binding 在同一事务中删除。
461
+ 使用 `yui config role unbind <global-role> <agent-id>` 或 `yui task role unbind <task-id> <role> <agent-id>` 可移除休眠 binding。active binding 或任何未 stopped 的 native session 都会被拒绝;stopped session 记录会和 binding 在同一事务中删除。
459
462
 
460
463
  Claude 的 session ID 在启动前分配。每个受管理的 Task Claude Run 都使用新的有限生命周期进程;resume 会针对固定 native session 启动新进程,而不是复用交互式 pane。受管理的 Codex 启动使用 Codex 结构化 `notify` 回调,在 turn 完成后记录 thread ID,不再向模型对话注入 session-bind prompt。
461
464
 
@@ -527,9 +530,11 @@ Web 端可以通过与 Terminal 相同的持久化 CLI 路径回答 open InputRe
527
530
 
528
531
  ```sh
529
532
  yui update
530
- yui agent add|list|show|capabilities|update|remove
531
- yui role add|list|show|update|remove|bind|enter
532
- yui role session record|replace
533
+ yui config agent add|list|show|capabilities|update|remove
534
+ yui config role add|list|show|update|remove|bind|unbind
535
+ yui config profile add|list|show|update|remove|reset
536
+ yui config completion [bash|zsh|fish]
537
+ yui session enter|record|replace|reconcile
533
538
  yui project add|clone|update|discover|list|show|knowledge
534
539
  ```
535
540
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zq-silk/yui",
3
- "version": "0.7.1",
3
+ "version": "0.8.1",
4
4
  "description": "Local control plane for long-running native agent CLI sessions backed by tmux.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -296,9 +296,12 @@ Choose before creating the WorkItem:
296
296
  credentials, user-owned independent Session, durable lifecycle, or repeated
297
297
  dispatches to a Task-bound Worker instance.
298
298
 
299
- Keep review execution separate from implementation. A reviewer uses the single
300
- built-in write-capable `reviewer` Profile, but Yui grants that capability only
301
- inside a fresh ReviewRound-owned worktree created from its exact frozen scope:
299
+ Keep review execution separate from implementation. No global Reviewer is
300
+ required: when review is disabled, inspect and decide directly or delegate a
301
+ bounded review to a native subagent or ordinary Worker. When a managed
302
+ ReviewRound is explicitly configured, its reviewer uses the single built-in
303
+ write-capable `reviewer` Profile, but Yui grants that capability only inside a
304
+ fresh ReviewRound-owned worktree created from its exact frozen scope:
302
305
  the assigned WorkItem Candidate or the committed Integration heads of a
303
306
  Task-final Review. Never reuse the Candidate/Worker workspace or its
304
307
  implementation Role Session. Codex and Claude may use their normal configured
@@ -319,7 +322,7 @@ Before the first delegated WorkItem, or after the Profile catalog changes,
319
322
  inspect the available Profiles:
320
323
 
321
324
  ```sh
322
- yui profile list
325
+ yui config profile list
323
326
  ```
324
327
 
325
328
  Choose the Profile by the work's meaning. `worker`, `implementer`, and
@@ -375,7 +378,7 @@ use `worker`. A Profile is required for this path:
375
378
 
376
379
  ```sh
377
380
  yui task work update <work-id> running
378
- yui profile show <worker|explorer|implementer|reviewer|profile-id>
381
+ yui config profile show <worker|explorer|implementer|reviewer|profile-id>
379
382
  ```
380
383
 
381
384
  Read the selected Profile and incorporate all applicable portable constraints
@@ -495,6 +498,10 @@ After any Candidate is submitted, inspect its exact policy, Run result,
495
498
  ReviewRounds, checks, and workspace through the exact Run Context Pack and its
496
499
  authorized expansions.
497
500
 
501
+ - No configured review policy: review the Candidate directly, or delegate a
502
+ bounded evidence-gathering review to a native subagent or ordinary Worker,
503
+ then make the Leader-owned accept/reject decision. Do not create a Reviewer
504
+ Role merely to satisfy an old setup convention.
498
505
  - `always`: wait for the automatically requested ReviewRound to become
499
506
  terminal. Never bypass an active round.
500
507
  - `leader`: decide whether the existing evidence is sufficient. Request Agent
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: yui-operator
3
- description: Route multi-project user requests into Yui Tasks, preserve durable intent, present progress, answer inputs, and administer lifecycle without taking over Leader decisions.
3
+ description: Route multi-project user requests into Yui Tasks, configure Yui through confirmed conversation, preserve durable intent, present progress, answer inputs, and administer lifecycle without taking over Leader decisions.
4
4
  ---
5
5
 
6
6
  # Yui Operator
@@ -46,6 +46,44 @@ mind and avoid imposing a fixed heading, field, section, or character
46
46
  template; one semantic event should have one concise summary unless a later
47
47
  role adds a genuinely new decision or impact.
48
48
 
49
+ ## Configure Yui through conversation
50
+
51
+ Treat configuration as an Operator-owned conversation, not a list of commands
52
+ the user must discover or run. Start every configuration discussion by reading
53
+ both the complete effective state and Yui's configuration catalog:
54
+
55
+ ```sh
56
+ yui --json config show
57
+ yui --json config describe
58
+ yui --json config describe <system|runtime|workflow|resources|tools|agent|role|profile|completion>
59
+ ```
60
+
61
+ Consume the top-level `data` field. Explain the relevant current values, what
62
+ each setting changes, its accepted values or referenced records, and when the
63
+ change takes effect. Distinguish stored values from effective defaults and say
64
+ when a live Role Session must be stopped and relaunched. Do not infer choices
65
+ from old setup conventions: Review, Worker, Profiles, and completion may
66
+ intentionally be absent after the minimum setup. Leader is part of the minimum
67
+ Task-execution configuration and must be present.
68
+
69
+ For Agent-dependent Role choices such as model, effort, provider permission,
70
+ search, settings source, or service tier, also read
71
+ `yui --json config agent capabilities <agent-id>`. Treat that live-or-cached
72
+ catalog and its freshness warnings as the choice authority; never invent a
73
+ provider value from memory.
74
+
75
+ When the user wants a change, narrow the discussion to the affected domains,
76
+ present the exact before/after behavior and material consequences, and obtain
77
+ confirmation before mutating configuration. Then perform only the confirmed
78
+ `yui config ...` commands yourself, read `yui --json config show` back, and
79
+ when the catalog says a Controller restart is required, include that impact in
80
+ the confirmation and run `yui controller restart` after saving. Report the
81
+ verified result. Never make the user execute mechanical CLI steps,
82
+ never parse human tables, never expose secret environment values, and never
83
+ silently create a Reviewer or enable global review. A Leader may review work
84
+ directly or delegate review to an ordinary Worker unless the user explicitly
85
+ configures a review Role and policy.
86
+
49
87
  ## Route across Projects and Tasks
50
88
 
51
89
  Inspect the catalog, Tasks, and global input Inbox before routing:
@@ -192,11 +230,11 @@ When the user requires a specific Leader or Worker provider, inspect Roles
192
230
  before routing:
193
231
 
194
232
  ```sh
195
- yui profile list
196
- yui profile show <profile>
197
- yui role list
198
- yui role show leader
199
- yui role show worker
233
+ yui config profile list
234
+ yui config profile show <profile>
235
+ yui config role list
236
+ yui config role show leader
237
+ yui config role show worker
200
238
  yui task role list <task-id>
201
239
  yui task work list <task-id>
202
240
  yui task integration list <task-id>
@@ -41,7 +41,7 @@ For a global Operator or custom GlobalRole Session, load the stable exact view
41
41
  before routing or acting:
42
42
 
43
43
  ```sh
44
- "$YUI_SESSION_CLI" role context "$YUI_ROLE" --json
44
+ "$YUI_SESSION_CLI" session context "$YUI_ROLE" --json
45
45
  ```
46
46
 
47
47
  Global context grants no Task implementation workspace. Read a Task only after