@zq-silk/yui 0.7.1 → 0.8.2
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.
- package/ARCHITECTURE.md +27 -28
- package/README.md +79 -71
- package/dist/cli/commandCatalog.js +283 -136
- package/dist/cli/completion.js +3 -3
- package/dist/cli/helpRenderer.js +3 -0
- package/dist/cli/interactionPolicy.js +48 -33
- package/dist/cli/interactiveSelection.js +1 -1
- package/dist/cli/invocationRouter.js +3 -2
- package/dist/cli/roleWizard.js +8 -8
- package/dist/cli.js +189 -93
- package/dist/commands/agentCommands.js +5 -5
- package/dist/commands/configCommands.js +351 -104
- package/dist/commands/configOverview.js +60 -0
- package/dist/commands/deliveryGuardPreflight.js +2 -2
- package/dist/commands/globalRoleCommands.js +9 -9
- package/dist/commands/profileCommands.js +8 -8
- package/dist/commands/resourcesCommands.js +6 -5
- package/dist/commands/taskCommands.js +111 -59
- package/dist/commands/taskRoleRuntimeStatus.js +3 -1
- package/dist/commands/telemetryCommands.js +11 -6
- package/dist/config/configCatalog.js +42 -0
- package/dist/config/yuiConfig.js +80 -35
- package/dist/context/sessionBootstrapManifest.js +1 -1
- package/dist/controller/clientRuntime.js +0 -2
- package/dist/controller/controller.js +21 -9
- package/dist/controller/fileSchedulerStoreAdapter.js +409 -79
- package/dist/controller/resourceInventory.js +9 -5
- package/dist/controller/runtime.js +112 -25
- package/dist/controller/runtimeLaunchCoordinator.js +18 -78
- package/dist/controller/structuredProviderObservation.js +273 -0
- package/dist/doctor/doctor.js +2 -2
- package/dist/executor/agentAdapter.js +40 -0
- package/dist/executor/agentExecutor.js +31 -7
- package/dist/executor/executorRegistry.js +11 -49
- package/dist/executor/fileRoleLaunchPlanner.js +115 -37
- package/dist/lifecycle/canonicalLifecycleEvent.js +5 -2
- package/dist/resources/autoResourceGc.js +3 -1
- package/dist/review/reviewConfig.js +0 -2
- package/dist/run/agentRun.js +2 -2
- package/dist/run/providerRetry.js +29 -16
- package/dist/run/providerRetryConfig.js +5 -3
- package/dist/runtime/agentHost.js +767 -158
- package/dist/runtime/builtinAgentDrivers.js +1 -5
- package/dist/runtime/codexAppServerRuntime.js +67 -60
- package/dist/runtime/exactControlPlane.js +7 -2
- package/dist/runtime/index.js +6 -2
- package/dist/runtime/launchBroker.js +30 -8
- package/dist/runtime/launchDiagnostics.js +1 -1
- package/dist/runtime/providerAuthorityFence.js +24 -0
- package/dist/runtime/providerControl.js +63 -0
- package/dist/runtime/providerRecoveryDecision.js +55 -0
- package/dist/runtime/providerRuntimeIdentity.js +269 -19
- package/dist/runtime/runtimeBinding.js +20 -11
- package/dist/runtime/structuredProviderHost.js +476 -0
- package/dist/runtime/tmuxAdapters.js +143 -42
- package/dist/scheduler/activeRoleRunDelivery.js +206 -120
- package/dist/scheduler/leaderWakeupProcessor.js +141 -16
- package/dist/scheduler/roleRunStall.js +12 -9
- package/dist/setup/setupCommand.js +153 -492
- package/dist/storage/compatibleTaskStore.js +9 -5
- package/dist/storage/migration/productionRegistry.js +169 -0
- package/dist/storage/taskStore.js +22 -3
- package/dist/telemetry/sqliteTelemetryStore.js +9 -1
- package/dist/telemetry/telemetryConfig.js +1 -18
- package/dist/telemetry/telemetryStore.js +2 -2
- package/dist/telemetry/telemetryWiring.js +6 -5
- package/dist/tmux/tmuxManager.js +1 -1
- package/dist/web/webSnapshot.js +5 -3
- package/i18n/README.zh-CN.md +48 -40
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +12 -5
- package/skills/yui-operator/SKILL.md +44 -6
- 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
|
|
16
|
-
*
|
|
17
|
-
* ordinary
|
|
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
|
|
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
|
}
|
|
@@ -79,8 +79,12 @@ const WORK_MAILBOX_FROM_VERSION = 1;
|
|
|
79
79
|
const WORK_MAILBOX_TO_VERSION = 2;
|
|
80
80
|
const TASK_ROLE_SESSION_SET_FROM_VERSION = 4;
|
|
81
81
|
const TASK_ROLE_SESSION_SET_TO_VERSION = 5;
|
|
82
|
+
const STRUCTURED_PROVIDER_SESSION_SET_FROM_VERSION = 5;
|
|
83
|
+
const STRUCTURED_PROVIDER_SESSION_SET_TO_VERSION = 6;
|
|
82
84
|
const PUBLICATION_REFERENCE_FROM_VERSION = 0;
|
|
83
85
|
const PUBLICATION_REFERENCE_TO_VERSION = 1;
|
|
86
|
+
const CONFIG_FROM_VERSION = 1;
|
|
87
|
+
const CONFIG_TO_VERSION = 2;
|
|
84
88
|
/**
|
|
85
89
|
* Build the authoritative production graph. Transition intent and executable
|
|
86
90
|
* transforms are registered together here; compatible loading and offline
|
|
@@ -129,6 +133,7 @@ export function createProductionStorageRegistry() {
|
|
|
129
133
|
declaredEffects: []
|
|
130
134
|
})
|
|
131
135
|
.registerOfflineMigration(projectOwnershipStep())
|
|
136
|
+
.registerOfflineMigration(configV2Step())
|
|
132
137
|
.registerCompatible(projectKnowledgeProposalsStep())
|
|
133
138
|
.registerOfflineMigration(taskWorkspaceIdentityStep())
|
|
134
139
|
.registerOfflineMigration(recordFamilyStep("workItem", WORK_ITEM_FROM_VERSION, WORK_ITEM_TO_VERSION, "workItems"))
|
|
@@ -158,10 +163,66 @@ export function createProductionStorageRegistry() {
|
|
|
158
163
|
.registerOfflineMigration(releaseWorkflowIntroductionStep())
|
|
159
164
|
.registerOfflineMigration(workMailboxV2Step())
|
|
160
165
|
.registerOfflineMigration(taskRoleSessionSetV5Step())
|
|
166
|
+
.registerOfflineMigration(structuredProviderSessionSetV6Step())
|
|
161
167
|
.registerOfflineMigration(publicationReferenceIntroductionStep());
|
|
162
168
|
assertRegistryCoversBaselineToCurrent(registry);
|
|
163
169
|
return registry;
|
|
164
170
|
}
|
|
171
|
+
function configV2Step() {
|
|
172
|
+
return {
|
|
173
|
+
axis: "record",
|
|
174
|
+
recordKind: "config",
|
|
175
|
+
fromVersion: CONFIG_FROM_VERSION,
|
|
176
|
+
toVersion: CONFIG_TO_VERSION,
|
|
177
|
+
preconditions: requireConfigV1,
|
|
178
|
+
transform: migrateConfigV1ToV2,
|
|
179
|
+
declaredEffects: []
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function requireConfigV1(snapshot) {
|
|
183
|
+
const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
|
|
184
|
+
if (versions.config !== CONFIG_FROM_VERSION) {
|
|
185
|
+
throw new Error(`Record config migration requires manifest version ${CONFIG_FROM_VERSION}.`);
|
|
186
|
+
}
|
|
187
|
+
if (snapshot.state === null)
|
|
188
|
+
return;
|
|
189
|
+
const config = asObject(snapshot.state.config, "Yui config");
|
|
190
|
+
if (config.schemaVersion !== CONFIG_FROM_VERSION) {
|
|
191
|
+
throw new Error(`Yui config must use schemaVersion ${CONFIG_FROM_VERSION} before migration.`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function migrateConfigV1ToV2(snapshot) {
|
|
195
|
+
requireConfigV1(snapshot);
|
|
196
|
+
const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
|
|
197
|
+
const schemaManifest = {
|
|
198
|
+
...snapshot.schemaManifest,
|
|
199
|
+
recordVersions: { ...versions, config: CONFIG_TO_VERSION }
|
|
200
|
+
};
|
|
201
|
+
if (snapshot.state === null)
|
|
202
|
+
return { schemaManifest, state: null };
|
|
203
|
+
const config = asObject(snapshot.state.config, "Yui config");
|
|
204
|
+
const { providerRetryMaxWindowMs, yieldReceiptReplay: _yieldReceiptReplay, gitBin: _gitBin, telemetryMode, schemaVersion: _schemaVersion, ...retained } = config;
|
|
205
|
+
const providerRetryMaxWindowSeconds = typeof providerRetryMaxWindowMs === "number"
|
|
206
|
+
? Math.ceil(providerRetryMaxWindowMs / 1_000)
|
|
207
|
+
: undefined;
|
|
208
|
+
const telemetryEnabled = telemetryMode === undefined
|
|
209
|
+
? undefined
|
|
210
|
+
: telemetryMode === "dual" || telemetryMode === "bounded";
|
|
211
|
+
return {
|
|
212
|
+
schemaManifest,
|
|
213
|
+
state: {
|
|
214
|
+
...snapshot.state,
|
|
215
|
+
config: {
|
|
216
|
+
...retained,
|
|
217
|
+
schemaVersion: CONFIG_TO_VERSION,
|
|
218
|
+
...(providerRetryMaxWindowSeconds === undefined
|
|
219
|
+
? {}
|
|
220
|
+
: { providerRetryMaxWindowSeconds }),
|
|
221
|
+
...(telemetryEnabled === undefined ? {} : { telemetryEnabled })
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
165
226
|
function workItemExecutionGroupHistoryStep() {
|
|
166
227
|
return {
|
|
167
228
|
axis: "record",
|
|
@@ -2167,6 +2228,114 @@ function taskRoleSessionSetV5Step() {
|
|
|
2167
2228
|
declaredEffects: []
|
|
2168
2229
|
};
|
|
2169
2230
|
}
|
|
2231
|
+
/**
|
|
2232
|
+
* v6 is a deliberate runtime cutover, not an emulation layer. Existing Task,
|
|
2233
|
+
* Run, Conversation, and Session identities remain as audit evidence, while
|
|
2234
|
+
* every pre-v6 managed process is terminalized locally. A new Agent Host must
|
|
2235
|
+
* establish structured Provider evidence before any further write.
|
|
2236
|
+
*/
|
|
2237
|
+
function structuredProviderSessionSetV6Step() {
|
|
2238
|
+
return {
|
|
2239
|
+
axis: "record",
|
|
2240
|
+
recordKind: "taskRoleSessionSet",
|
|
2241
|
+
fromVersion: STRUCTURED_PROVIDER_SESSION_SET_FROM_VERSION,
|
|
2242
|
+
toVersion: STRUCTURED_PROVIDER_SESSION_SET_TO_VERSION,
|
|
2243
|
+
preconditions: requireTaskRoleSessionSetV5Family,
|
|
2244
|
+
transform: migrateTaskRoleSessionSetV5ToV6,
|
|
2245
|
+
declaredEffects: []
|
|
2246
|
+
};
|
|
2247
|
+
}
|
|
2248
|
+
function requireTaskRoleSessionSetV5Family(snapshot) {
|
|
2249
|
+
const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
|
|
2250
|
+
if (versions.taskRoleSessionSet !== STRUCTURED_PROVIDER_SESSION_SET_FROM_VERSION) {
|
|
2251
|
+
throw new Error(`Record taskRoleSessionSet migration requires manifest version ${STRUCTURED_PROVIDER_SESSION_SET_FROM_VERSION}.`);
|
|
2252
|
+
}
|
|
2253
|
+
if (snapshot.state === null)
|
|
2254
|
+
return;
|
|
2255
|
+
const tasks = asObject(snapshot.state.tasks, "state tasks");
|
|
2256
|
+
for (const [taskId, rawAggregate] of Object.entries(tasks)) {
|
|
2257
|
+
const aggregate = asObject(rawAggregate, `Task aggregate ${taskId}`);
|
|
2258
|
+
const sets = asObject(aggregate.roleSessionSets, `Task Role session sets ${taskId}`);
|
|
2259
|
+
for (const [roleName, rawSet] of Object.entries(sets)) {
|
|
2260
|
+
const set = asObject(rawSet, `Task Role session set ${taskId}/${roleName}`);
|
|
2261
|
+
if (set.schemaVersion !== STRUCTURED_PROVIDER_SESSION_SET_FROM_VERSION) {
|
|
2262
|
+
throw new Error(`Task Role session set ${taskId}/${roleName} must use schemaVersion ${STRUCTURED_PROVIDER_SESSION_SET_FROM_VERSION}.`);
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
function migrateTaskRoleSessionSetV5ToV6(snapshot) {
|
|
2268
|
+
requireTaskRoleSessionSetV5Family(snapshot);
|
|
2269
|
+
const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
|
|
2270
|
+
const schemaManifest = {
|
|
2271
|
+
...snapshot.schemaManifest,
|
|
2272
|
+
recordVersions: {
|
|
2273
|
+
...versions,
|
|
2274
|
+
taskRoleSessionSet: STRUCTURED_PROVIDER_SESSION_SET_TO_VERSION
|
|
2275
|
+
}
|
|
2276
|
+
};
|
|
2277
|
+
if (snapshot.state === null)
|
|
2278
|
+
return { schemaManifest, state: null };
|
|
2279
|
+
const tasks = asObject(snapshot.state.tasks, "state tasks");
|
|
2280
|
+
const nextTasks = {};
|
|
2281
|
+
for (const [taskId, rawAggregate] of Object.entries(tasks)) {
|
|
2282
|
+
const aggregate = asObject(rawAggregate, `Task aggregate ${taskId}`);
|
|
2283
|
+
const rawSets = asObject(aggregate.roleSessionSets, `Task Role session sets ${taskId}`);
|
|
2284
|
+
const nextSets = {};
|
|
2285
|
+
for (const [roleName, rawSet] of Object.entries(rawSets)) {
|
|
2286
|
+
const set = asObject(rawSet, `Task Role session set ${taskId}/${roleName}`);
|
|
2287
|
+
const invalidatedAt = String(set.updatedAt);
|
|
2288
|
+
const sessions = asObject(set.sessions, `Task Role sessions ${taskId}/${roleName}`);
|
|
2289
|
+
const nextSessions = Object.fromEntries(Object.entries(sessions).map(([agentId, rawSession]) => {
|
|
2290
|
+
const session = asObject(rawSession, `Task Role Session ${taskId}/${roleName}/${agentId}`);
|
|
2291
|
+
return [agentId, session.status === "stopped" || session.status === "broken"
|
|
2292
|
+
? session
|
|
2293
|
+
: { ...session, status: "broken", updatedAt: invalidatedAt }];
|
|
2294
|
+
}));
|
|
2295
|
+
nextSets[roleName] = {
|
|
2296
|
+
...set,
|
|
2297
|
+
schemaVersion: STRUCTURED_PROVIDER_SESSION_SET_TO_VERSION,
|
|
2298
|
+
sessions: nextSessions,
|
|
2299
|
+
providerBinding: set.providerBinding === null
|
|
2300
|
+
? null
|
|
2301
|
+
: invalidateLegacyProviderBinding(asObject(set.providerBinding, `Provider Binding ${taskId}/${roleName}`), invalidatedAt)
|
|
2302
|
+
};
|
|
2303
|
+
}
|
|
2304
|
+
nextTasks[taskId] = { ...aggregate, roleSessionSets: nextSets };
|
|
2305
|
+
}
|
|
2306
|
+
return { schemaManifest, state: { ...snapshot.state, tasks: nextTasks } };
|
|
2307
|
+
}
|
|
2308
|
+
function invalidateLegacyProviderBinding(binding, invalidatedAt) {
|
|
2309
|
+
if (binding.schemaVersion !== 1) {
|
|
2310
|
+
throw new Error("Pre-v6 Provider Runtime Binding must use schemaVersion 1.");
|
|
2311
|
+
}
|
|
2312
|
+
const activations = Array.isArray(binding.activations) ? binding.activations : [];
|
|
2313
|
+
return {
|
|
2314
|
+
...binding,
|
|
2315
|
+
schemaVersion: 2,
|
|
2316
|
+
conversations: (Array.isArray(binding.conversations) ? binding.conversations : []).map((rawConversation) => {
|
|
2317
|
+
const conversation = asObject(rawConversation, "Provider Conversation");
|
|
2318
|
+
return conversation.status === "current"
|
|
2319
|
+
? { ...conversation, recoverability: "unknown" }
|
|
2320
|
+
: conversation;
|
|
2321
|
+
}),
|
|
2322
|
+
activations: activations.map((rawActivation) => {
|
|
2323
|
+
const activation = asObject(rawActivation, "Provider Activation");
|
|
2324
|
+
const { writerLease: _removed, ...withoutLease } = activation;
|
|
2325
|
+
void _removed;
|
|
2326
|
+
return activation.status === "active"
|
|
2327
|
+
? {
|
|
2328
|
+
...withoutLease,
|
|
2329
|
+
status: "failed",
|
|
2330
|
+
endedAt: invalidatedAt,
|
|
2331
|
+
terminalReason: "legacy-managed-runtime-invalidated"
|
|
2332
|
+
}
|
|
2333
|
+
: withoutLease;
|
|
2334
|
+
}),
|
|
2335
|
+
authority: { epoch: 1, owner: "none", changedAt: invalidatedAt },
|
|
2336
|
+
turn: null
|
|
2337
|
+
};
|
|
2338
|
+
}
|
|
2170
2339
|
function requireTaskRoleSessionSetV4Family(snapshot) {
|
|
2171
2340
|
const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
|
|
2172
2341
|
if (versions.taskRoleSessionSet !== TASK_ROLE_SESSION_SET_FROM_VERSION) {
|
|
@@ -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 =
|
|
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
|
/**
|
|
@@ -117,7 +117,7 @@ export const CURRENT_STORED_TASK_SCHEMA_VERSION = 17;
|
|
|
117
117
|
* Keep these named at the storage boundary so the upgrade record-axis map can
|
|
118
118
|
* assert it is classifying the same bytes the store reads and writes.
|
|
119
119
|
*/
|
|
120
|
-
export const CURRENT_TASK_ROLE_SESSION_SET_SCHEMA_VERSION =
|
|
120
|
+
export const CURRENT_TASK_ROLE_SESSION_SET_SCHEMA_VERSION = 6;
|
|
121
121
|
/**
|
|
122
122
|
* v7 combines optional Issue 04 retry/receipt fields and Issue 05 Leader
|
|
123
123
|
* actionability fields. All are optional, so the v6→v7 migration is a
|
|
@@ -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 ?? "
|
|
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 = "
|
|
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
|
|
1
|
+
/** No-op sink for disabled telemetry and callers without a sidecar. */
|
|
2
2
|
export class NullTelemetrySink {
|
|
3
3
|
mode;
|
|
4
|
-
constructor(mode = "
|
|
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,
|
|
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
|
-
|
|
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(`
|
|
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
|
}
|
package/dist/tmux/tmuxManager.js
CHANGED
|
@@ -1025,7 +1025,7 @@ export class TmuxManager {
|
|
|
1025
1025
|
/**
|
|
1026
1026
|
* Pin the Role window to the largest attached client. The tmux default
|
|
1027
1027
|
* `window-size latest` lets a later-attaching smaller client (e.g. the Web
|
|
1028
|
-
* terminal or a second
|
|
1028
|
+
* terminal or a second Role viewer from a smaller pane) shrink the shared
|
|
1029
1029
|
* Role window, leaving the primary viewer with a TUI pinned to the top of a
|
|
1030
1030
|
* large terminal and no scrollback. `largest` keeps the window at the
|
|
1031
1031
|
* biggest attached client so a compact viewer cannot compress it.
|
package/dist/web/webSnapshot.js
CHANGED
|
@@ -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,
|
package/i18n/README.zh-CN.md
CHANGED
|
@@ -45,31 +45,34 @@ yui setup
|
|
|
45
45
|
yui doctor
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
`setup`
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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 能力
|
|
@@ -432,20 +435,23 @@ Task 生命周期的交互选择只展示有效来源状态:activate 只展示
|
|
|
432
435
|
|
|
433
436
|
## Session 与 tmux
|
|
434
437
|
|
|
435
|
-
|
|
438
|
+
受管理的 Task Agent 统一使用混合 Provider Runtime:Controller 与 Agent Host 通过 Provider 原生结构化协议提交和确认输入;tmux/PTY 只负责保持 Host 存活、展示输出,以及在显式人工接管后提供输入网关。受管理输入绝不会作为终端按键、粘贴文本或启动 argv 发送。Codex 使用持久 App Server JSON-RPC 进程;Claude 使用持久 stream-json 进程,并以精确回放的 user message 作为接收确认。
|
|
436
439
|
|
|
437
|
-
|
|
440
|
+
Run、Conversation、Activation 与 Turn 是四个独立身份。Conversation 可以跨多个 Run 和进程;Activation 只代表一次 Provider 进程存活期;Turn 在写入前先持久化。写入超时或结果不明确会进入 `delivery-unknown`,不会自动重发。Provider 进程退出后结束当前 Activation;恢复同一 Conversation 会创建新 Activation 并推进 authority epoch。
|
|
438
441
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
Global 交互入口在不存在 writer 时保持可写;已有 writer 时自动降级为只读。global Web 对每个 tmux session 只允许一个 writer;Task Web 始终只读。Task CLI 入口除非显式请求 `--read-write`,否则始终只读,避免观察动作改变 Agent 执行。
|
|
442
|
+
Task Role 使用以下显式入口:
|
|
442
443
|
|
|
443
444
|
```sh
|
|
444
|
-
yui
|
|
445
|
-
yui task
|
|
446
|
-
yui task role
|
|
445
|
+
yui session enter <global-role>
|
|
446
|
+
yui task role view <task-id> <role>
|
|
447
|
+
yui task role takeover <task-id> <role>
|
|
448
|
+
yui task role release <task-id> <role>
|
|
447
449
|
```
|
|
448
450
|
|
|
451
|
+
`view` 始终只读。`takeover` 要求存在 active managed Run 且没有未决 Turn;它先以持久 CAS 把唯一 writer authority 转给人工 holder,再把相同 epoch 同步给 Agent Host,最后开放 PTY 输入网关。人工输入仍由 Host 转换为结构化 Provider Turn,而不是直接注入 Provider 终端。detach 会自动归还 authority;`release` 即使没有 active Run 也可执行,用于幂等修复中断或未完全同步的接管。Global Operator 与 global Role 继续使用原生交互式 CLI,不属于受管理 Task Provider 协议。
|
|
452
|
+
|
|
453
|
+
tmux 会在 pane 创建时固定其历史容量。配置该限制之前创建的 Role 会保留原容量;Yui 会在 Terminal attach 和 Web 中提示用户退出并重新进入一次,从而创建具有 100,000 行历史的新 pane。
|
|
454
|
+
|
|
449
455
|
每个 Role 可绑定多个 Agent,但任一时刻只有一个 active Agent,并为每个
|
|
450
456
|
Agent binding 独立保存 native session。Operator 进一步限制为同一种
|
|
451
457
|
adapter 最多绑定一个,例如可同时绑定一个 Codex 和一个 Claude;这些
|
|
@@ -455,9 +461,9 @@ binding 是预先保存、可随时切换的配置,而不是并行身份。Ope
|
|
|
455
461
|
并切换。跨 Agent 切换默认复用已保存的 model/effort,只有用户明确选择
|
|
456
462
|
更新时才进入现有配置选择流程。
|
|
457
463
|
|
|
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 在同一事务中删除。
|
|
464
|
+
使用 `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
465
|
|
|
460
|
-
Claude 的 session ID
|
|
466
|
+
Claude 的 session ID 在启动前分配,并由持久 stream-json Provider 进程承载多个 Turn;Codex 使用持久 App Server thread。两者都复用同一套 Conversation、Activation、Turn 与 authority fence,不再向模型对话注入 session-bind prompt。
|
|
461
467
|
|
|
462
468
|
自动生命周期与投递判断只使用结构化 Hook payload、持久身份、tmux process
|
|
463
469
|
state、receipt 与 pane fence。Yui 不会解析 prompt glyph、进度文本、trust dialog
|
|
@@ -527,9 +533,11 @@ Web 端可以通过与 Terminal 相同的持久化 CLI 路径回答 open InputRe
|
|
|
527
533
|
|
|
528
534
|
```sh
|
|
529
535
|
yui update
|
|
530
|
-
yui agent add|list|show|capabilities|update|remove
|
|
531
|
-
yui role add|list|show|update|remove|bind|
|
|
532
|
-
yui
|
|
536
|
+
yui config agent add|list|show|capabilities|update|remove
|
|
537
|
+
yui config role add|list|show|update|remove|bind|unbind
|
|
538
|
+
yui config profile add|list|show|update|remove|reset
|
|
539
|
+
yui config completion [bash|zsh|fish]
|
|
540
|
+
yui session enter|record|replace|reconcile
|
|
533
541
|
yui project add|clone|update|discover|list|show|knowledge
|
|
534
542
|
```
|
|
535
543
|
|