@zq-silk/yui 0.14.0 → 0.14.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 +64 -35
- package/README.md +258 -125
- package/dist/agent/argumentPolicy.js +2 -1
- package/dist/agent/managedRuntimeEnvironment.js +5 -8
- package/dist/cli/commandCatalog.js +74 -109
- package/dist/cli/helpRenderer.js +1 -2
- package/dist/cli/interactionCandidates.js +10 -0
- package/dist/cli/interactionPolicy.js +76 -37
- package/dist/cli/roleWizard.js +5 -6
- package/dist/cli/updateOrchestrator.js +10 -8
- package/dist/cli/updatePorts.js +17 -13
- package/dist/cli/upgradeCommand.js +2 -5
- package/dist/cli.js +268 -231
- package/dist/commands/agentCommands.js +13 -0
- package/dist/commands/configCommands.js +7 -19
- package/dist/commands/configOverview.js +3 -2
- package/dist/commands/durableJobCommands.js +1 -1
- package/dist/commands/executionAuditCommands.js +4 -9
- package/dist/commands/globalRoleCommands.js +17 -0
- package/dist/commands/profileCommands.js +200 -30
- package/dist/commands/sessionCommands.js +1 -1
- package/dist/commands/taskActor.js +29 -127
- package/dist/commands/taskChangeSetCommands.js +1 -3
- package/dist/commands/taskCommands.js +1771 -2107
- package/dist/commands/taskCompletionGate.js +27 -103
- package/dist/commands/taskContextCommand.js +47 -80
- package/dist/commands/taskExecutionCommands.js +9 -27
- package/dist/commands/taskInputCommands.js +10 -10
- package/dist/commands/taskIntegrationCommands.js +69 -61
- package/dist/commands/taskIntegrationQueueCommands.js +1 -1
- package/dist/commands/taskNextActionCommand.js +8 -29
- package/dist/commands/taskOverviewCommand.js +25 -3
- package/dist/commands/taskPublicationCommands.js +151 -85
- package/dist/commands/taskPublicationVerifyCommand.js +181 -0
- package/dist/commands/taskRemoteDeliveryCommand.js +134 -0
- package/dist/commands/taskRoleRuntimeStatus.js +5 -6
- package/dist/commands/taskUpstreamCommands.js +79 -95
- package/dist/config/configCatalog.js +1 -1
- package/dist/context/contextSnapshot.js +17 -2
- package/dist/context/sessionBootstrapManifest.js +18 -1
- package/dist/context/sourceTurnContext.js +30 -0
- package/dist/context/turnContextPack.js +107 -48
- package/dist/context/turnInputContract.js +11 -4
- package/dist/context/wakeNotification.js +84 -22
- package/dist/controller/agentRuntimeObserver.js +9 -12
- package/dist/controller/clientRuntime.js +13 -13
- package/dist/controller/controller.js +11 -11
- package/dist/controller/fileSchedulerStoreAdapter.js +262 -219
- package/dist/controller/jobControl.js +11 -10
- package/dist/controller/resourceInventory.js +6 -6
- package/dist/controller/resourceInventoryLinux.js +2 -2
- package/dist/controller/runtime.js +18 -61
- package/dist/controller/runtimeEventInbox.js +57 -34
- package/dist/controller/runtimeEventProcessor.js +36 -96
- package/dist/controller/runtimeHookTurnFence.js +33 -57
- package/dist/controller/runtimeLaunchCoordinator.js +44 -40
- package/dist/controller/runtimeObservationHook.js +18 -12
- package/dist/controller/sessionNotify.js +43 -28
- package/dist/controller/sessionOwnerReconciliation.js +21 -21
- package/dist/controller/structuredProviderObservation.js +13 -16
- package/dist/coordination/workMailboxQueue.js +132 -0
- package/dist/core/controllerServer.js +1 -0
- package/dist/domain/agentResultTransport.js +60 -0
- package/dist/execution/executionHealth.js +111 -118
- package/dist/execution/reviewMainTurn.js +161 -0
- package/dist/execution/workItemExecution.js +454 -0
- package/dist/execution/workItemExecutionProjection.js +309 -0
- package/dist/execution/workItemMainTurn.js +164 -0
- package/dist/executor/agentExecutor.js +19 -19
- package/dist/executor/executorRegistry.js +7 -28
- package/dist/executor/fileRoleLaunchPlanner.js +51 -83
- package/dist/external/githubPublicationVerifier.js +114 -0
- package/dist/external/gitlabPublicationVerifier.js +158 -0
- package/dist/external/pinnedCommandRunner.js +73 -0
- package/dist/integration/changeSetManifest.js +0 -3
- package/dist/integration/deliveryObligation.js +43 -6
- package/dist/integration/gitIntegrationService.js +284 -101
- package/dist/integration/integrationAttempt.js +64 -14
- package/dist/integration/integrationQueueEntry.js +5 -35
- package/dist/integration/integrationQueueService.js +65 -194
- package/dist/lifecycle/canonicalLifecycleEvent.js +16 -15
- package/dist/lifecycle/exactTurnTerminalization.js +100 -97
- package/dist/message/message.js +16 -0
- package/dist/observability/executionAudit.js +4 -25
- package/dist/observability/faultClassification.js +58 -70
- package/dist/observability/orchestrationMetrics.js +8 -50
- package/dist/profile/agentProfile.js +60 -15
- package/dist/profile/agentProfileRuntime.js +100 -0
- package/dist/release/releaseWorkflowPorts.js +5 -68
- package/dist/repository/executionLaneGitSnapshot.js +80 -0
- package/dist/repository/gitWorkspace.js +9 -67
- package/dist/repository/taskBaseFreshness.js +3 -1
- package/dist/repository/taskWorkspaceCoordinator.js +32 -4
- package/dist/repository/taskWorkspacePreparer.js +252 -411
- package/dist/resources/liveReferences.js +6 -55
- package/dist/resources/resourceDiscovery.js +1 -30
- package/dist/resources/resourceRegistrar.js +0 -23
- package/dist/review/deltaRecheck.js +8 -65
- package/dist/review/reviewAcceptance.js +22 -19
- package/dist/review/reviewConfig.js +0 -10
- package/dist/review/reviewDecision.js +2 -2
- package/dist/review/reviewRound.js +147 -273
- package/dist/review/reviewerAvailability.js +13 -23
- package/dist/role/role.js +3 -0
- package/dist/runtime/agentDriverObservation.js +1 -1
- package/dist/runtime/agentHost.js +51 -51
- package/dist/runtime/builtinAgentDrivers.js +9 -2
- package/dist/runtime/codexAppServerRuntime.js +23 -5
- package/dist/runtime/exactControlPlane.js +2 -267
- package/dist/runtime/firstProgressAdvisory.js +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/launchBroker.js +11 -11
- package/dist/runtime/lifecycleReservation.js +2 -2
- package/dist/runtime/managedCaller.js +111 -0
- package/dist/runtime/ports.js +16 -3
- package/dist/runtime/preallocatedNativeSession.js +2 -2
- package/dist/runtime/processExitObservation.js +3 -2
- package/dist/runtime/providerContinuationReconciliationService.js +1 -1
- package/dist/runtime/runtimeBinding.js +1 -1
- package/dist/runtime/runtimeObservation.js +33 -16
- package/dist/runtime/runtimeSessionCandidate.js +2 -2
- package/dist/runtime/runtimeStopReceipt.js +11 -11
- package/dist/runtime/sessionLaunchRequest.js +2 -2
- package/dist/runtime/sessionOwnerIdentity.js +9 -9
- package/dist/runtime/sessionOwnerRegistry.js +13 -13
- package/dist/runtime/sessionReconciliation.js +4 -4
- package/dist/runtime/sessionTerminationGuard.js +9 -9
- package/dist/runtime/sessionTokenMetrics.js +5 -7
- package/dist/runtime/structuredProviderHost.js +5 -5
- package/dist/runtime/taskRuntimeIsolation.js +24 -24
- package/dist/runtime/tmuxAdapters.js +32 -30
- package/dist/scheduler/activeRoleTurnDelivery.js +37 -15
- package/dist/scheduler/leaderWakeupProcessor.js +78 -55
- package/dist/scheduler/roleTurnLiveness.js +6 -6
- package/dist/scheduler/roleTurnStall.js +8 -9
- package/dist/scheduler/taskExecutionProjection.js +19 -41
- package/dist/scheduler/taskObservabilityProjection.js +75 -122
- package/dist/storage/sqliteSchema.js +1 -21
- package/dist/storage/sqliteStore.js +62 -47
- package/dist/storage/storageSchema.js +1 -8
- package/dist/storage/storageVersions.js +7 -4
- package/dist/storage/taskStore.js +8 -8
- package/dist/storage/upgrade/upgradeOrchestrator.js +112 -83
- package/dist/task/completionReadiness.js +17 -98
- package/dist/task/deliveryGuard.js +9 -11
- package/dist/task/draftPlan.js +142 -0
- package/dist/task/nextAction.js +179 -477
- package/dist/task/publicationVerification.js +1 -0
- package/dist/task/remoteDelivery.js +233 -0
- package/dist/task/task.js +108 -4
- package/dist/task/taskRecordReference.js +0 -1
- package/dist/turn/turn.js +109 -16
- package/dist/verification/verificationGateService.js +0 -26
- package/dist/web/assets/client/components.js +99 -121
- package/dist/web/assets/client/i18n.js +136 -58
- package/dist/web/assets/client/view.js +44 -0
- package/dist/web/assets/styles/cards.js +3 -15
- package/dist/web/assets/styles/widgets.js +4 -0
- package/dist/web/webSnapshot.js +29 -14
- package/dist/workItem/dependencyGate.js +34 -0
- package/dist/workItem/workItem.js +99 -266
- package/dist/workspace/workItemChangeSetManager.js +15 -59
- package/docs/task-local-identity.md +1 -1
- package/i18n/README.zh-CN.md +165 -59
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +272 -782
- package/skills/yui-operator/SKILL.md +190 -365
- package/skills/yui-reviewer/SKILL.md +40 -33
- package/skills/yui-runtime/SKILL.md +27 -6
- package/skills/yui-worker/SKILL.md +19 -6
- package/dist/execution/candidateConvergence.js +0 -623
- package/dist/execution/executionGroup.js +0 -854
- package/dist/execution/resourceBroker.js +0 -426
- package/dist/executor/launchPlan.js +0 -16
- package/dist/review/reviewFinding.js +0 -240
- package/dist/review/reviewFindingLedger.js +0 -550
- package/dist/review/reviewOutcomeClassifier.js +0 -255
- package/dist/scheduler/resourceQueueProjection.js +0 -71
- package/dist/task/repairWave.js +0 -150
|
@@ -119,6 +119,11 @@ function updateAgent(args, store) {
|
|
|
119
119
|
+ "Stop that Role session before changing Agent launch settings.");
|
|
120
120
|
}
|
|
121
121
|
if (changes.adapter) {
|
|
122
|
+
const profile = findAgentProfileReference(tx, id);
|
|
123
|
+
if (profile !== null) {
|
|
124
|
+
throw usageError(`Agent ${id} adapter cannot change because Agent Profile ${profile.id} references it. `
|
|
125
|
+
+ "Update that explicit Profile or create a new Agent ID instead.");
|
|
126
|
+
}
|
|
122
127
|
const binding = findRoleBindingReference(tx, id);
|
|
123
128
|
if (binding !== null) {
|
|
124
129
|
throw usageError(`Agent ${id} adapter cannot change because ${describeReference(binding)} references it. `
|
|
@@ -156,6 +161,11 @@ function removeAgent(args, store) {
|
|
|
156
161
|
throw usageError(`Agent ${id} cannot be removed because ${describeReference(binding)} references it. `
|
|
157
162
|
+ "Migrate or remove that Role binding before removing this Agent.");
|
|
158
163
|
}
|
|
164
|
+
const profile = findAgentProfileReference(tx, id);
|
|
165
|
+
if (profile !== null) {
|
|
166
|
+
throw usageError(`Agent ${id} cannot be removed because Agent Profile ${profile.id} references it. `
|
|
167
|
+
+ "Update, inherit, or remove that Profile first.");
|
|
168
|
+
}
|
|
159
169
|
const session = findNonStoppedSessionReference(tx, id);
|
|
160
170
|
if (session !== null) {
|
|
161
171
|
throw usageError(`Agent ${id} cannot be removed because ${describeReference(session)} `
|
|
@@ -227,6 +237,9 @@ function findRoleBindingReference(store, agentId) {
|
|
|
227
237
|
}
|
|
228
238
|
return null;
|
|
229
239
|
}
|
|
240
|
+
function findAgentProfileReference(store, agentId) {
|
|
241
|
+
return store.listAgentProfiles().find((profile) => profile.runtime.source === "explicit" && profile.runtime.agentId === agentId) ?? null;
|
|
242
|
+
}
|
|
230
243
|
function findNonStoppedSessionReference(store, agentId) {
|
|
231
244
|
for (const set of store.listGlobalRoleSessionSets()) {
|
|
232
245
|
const reference = sessionReference(set, agentId, { scope: "global", roleName: set.owner.roleName });
|
|
@@ -5,7 +5,7 @@ import { DEFAULT_AGENT_LAUNCH_INACTIVITY_TIMEOUT_SECONDS, DEFAULT_CONTROLLER_TAS
|
|
|
5
5
|
import { CONFIG_DEFINITIONS, CONFIG_KEYS, configDefinition, configDefinitionsForDomain } from "../config/configCatalog.js";
|
|
6
6
|
import { resolveTimeZone } from "../output/timePresentation.js";
|
|
7
7
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
8
|
-
import {
|
|
8
|
+
import { REVIEW_TRIGGERS } from "../review/reviewConfig.js";
|
|
9
9
|
/**
|
|
10
10
|
* One uniform key model for every durable setting. Each catalog domain uses
|
|
11
11
|
* the same show/set/clear contract; strategy settings such as `review` and
|
|
@@ -21,8 +21,7 @@ const RECONCILIATION_SET_USAGE = "Runtime config set usage: yui config runtime s
|
|
|
21
21
|
const RESOURCES_GC_MODE_SET_USAGE = "Resources config set usage: yui config resources set resources-gc-mode <report|quarantine>.";
|
|
22
22
|
const RESOURCES_GC_AUTO_QUARANTINE_SET_USAGE = "Resources config set usage: yui config resources set resources-gc-auto-quarantine <true|false>.";
|
|
23
23
|
const LEADER_NEXT_ACTION_SET_USAGE = `Workflow config set usage: yui config workflow set leader-next-action <${LEADER_NEXT_ACTION_MODES.join("|")}>.`;
|
|
24
|
-
const REVIEW_SET_USAGE = "Workflow config set usage: yui config workflow set review --role <global-role> --trigger <always|leader|final
|
|
25
|
-
+ "[--finding-ledger <shadow|enforce>].";
|
|
24
|
+
const REVIEW_SET_USAGE = "Workflow config set usage: yui config workflow set review --role <global-role> --trigger <always|leader|final>.";
|
|
26
25
|
export function runConfigCommand(domain, args, store) {
|
|
27
26
|
const keys = configDefinitionsForDomain(domain).map(({ key }) => key);
|
|
28
27
|
const usage = `${capitalize(domain)} config usage: yui config ${domain} show | yui config ${domain} set <key> <value...> | yui config ${domain} clear <key>.`;
|
|
@@ -88,8 +87,7 @@ export function effectiveConfigData(config, domain) {
|
|
|
88
87
|
? null
|
|
89
88
|
: {
|
|
90
89
|
roleName: config.review.roleName,
|
|
91
|
-
trigger: config.review.trigger
|
|
92
|
-
findingLedger: config.review.findingLedger ?? "shadow"
|
|
90
|
+
trigger: config.review.trigger
|
|
93
91
|
},
|
|
94
92
|
completionInstallations: config.completionInstallations ?? {}
|
|
95
93
|
};
|
|
@@ -545,8 +543,7 @@ const CONFIG_KEY_HANDLERS = [
|
|
|
545
543
|
showLabel: "Review",
|
|
546
544
|
showValue: (config) => (config.review === undefined
|
|
547
545
|
? "disabled"
|
|
548
|
-
: `${config.review.roleName} (${config.review.trigger}
|
|
549
|
-
+ ")"),
|
|
546
|
+
: `${config.review.roleName} (${config.review.trigger})`),
|
|
550
547
|
set(args, store) {
|
|
551
548
|
if (args.length < 4 || args.length % 2 !== 0)
|
|
552
549
|
throw usageError(REVIEW_SET_USAGE);
|
|
@@ -554,7 +551,7 @@ const CONFIG_KEY_HANDLERS = [
|
|
|
554
551
|
for (let index = 0; index < args.length; index += 2) {
|
|
555
552
|
const name = args[index];
|
|
556
553
|
const value = args[index + 1];
|
|
557
|
-
if (!["--role", "--trigger"
|
|
554
|
+
if (!["--role", "--trigger"].includes(name)
|
|
558
555
|
|| value === undefined
|
|
559
556
|
|| options.has(name)) {
|
|
560
557
|
throw usageError(REVIEW_SET_USAGE);
|
|
@@ -572,23 +569,14 @@ const CONFIG_KEY_HANDLERS = [
|
|
|
572
569
|
throw usageError(`Global Role not found: ${roleName}.`);
|
|
573
570
|
}
|
|
574
571
|
const trigger = rawTrigger;
|
|
575
|
-
const rawLedgerMode = options.get("--finding-ledger")?.trim();
|
|
576
|
-
let findingLedger;
|
|
577
|
-
if (rawLedgerMode !== undefined) {
|
|
578
|
-
if (!REVIEW_FINDING_LEDGER_MODES.includes(rawLedgerMode)) {
|
|
579
|
-
throw usageError(REVIEW_SET_USAGE);
|
|
580
|
-
}
|
|
581
|
-
findingLedger = rawLedgerMode;
|
|
582
|
-
}
|
|
583
572
|
saveConfigKey(store, (config) => ({
|
|
584
573
|
...config,
|
|
585
574
|
review: {
|
|
586
575
|
roleName,
|
|
587
|
-
trigger
|
|
588
|
-
...(findingLedger === undefined ? {} : { findingLedger })
|
|
576
|
+
trigger
|
|
589
577
|
}
|
|
590
578
|
}));
|
|
591
|
-
return `Review set to ${roleName} (${trigger}
|
|
579
|
+
return `Review set to ${roleName} (${trigger})\n`;
|
|
592
580
|
},
|
|
593
581
|
clear(store) {
|
|
594
582
|
saveConfigKey(store, (config) => {
|
|
@@ -25,13 +25,14 @@ export function runConfigOverview(args, store, environment, identity, roleOption
|
|
|
25
25
|
name,
|
|
26
26
|
store.getGlobalRole(name)
|
|
27
27
|
]));
|
|
28
|
-
const
|
|
28
|
+
const profileResult = runProfileCommand(["list"], store);
|
|
29
|
+
const profiles = profileResult.data.profiles;
|
|
29
30
|
const completion = inspectCompletionStates(store.getConfig(), environment, identity);
|
|
30
31
|
const roleOutput = runGlobalRoleCommand(["list"], store, roleOptions);
|
|
31
32
|
if (typeof roleOutput !== "string") {
|
|
32
33
|
throw new Error("Config overview received an invalid Role control result.");
|
|
33
34
|
}
|
|
34
|
-
const profileOutput =
|
|
35
|
+
const profileOutput = profileResult.output;
|
|
35
36
|
const agentOutput = runAgentCommand(["list"], store);
|
|
36
37
|
const domainData = Object.fromEntries(CONFIG_DOMAINS.map((domain) => [
|
|
37
38
|
domain,
|
|
@@ -99,7 +99,7 @@ async function acknowledgeJob(args, options) {
|
|
|
99
99
|
if (options.store === undefined) {
|
|
100
100
|
throw usageError("job acknowledge requires a Task store to resolve the caller.");
|
|
101
101
|
}
|
|
102
|
-
taskLocalActor(options.store, options.environment, ref.taskId
|
|
102
|
+
taskLocalActor(options.store, options.environment, ref.taskId);
|
|
103
103
|
const caller = resolveJobCaller(options.environment, ref.taskId);
|
|
104
104
|
await ensureFileTaskController(options.home, { environment: options.environment });
|
|
105
105
|
const result = await acknowledgeDurableJob(options.home, ref.taskId, ref.jobId, caller);
|
|
@@ -150,14 +150,9 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
|
|
|
150
150
|
}
|
|
151
151
|
if (report.reviews.status === "ok" && report.reviews.data !== undefined) {
|
|
152
152
|
const reviews = report.reviews.data;
|
|
153
|
-
lines.push("", `Review rounds: ${reviews.total} total · ${reviews.completed} completed · ${reviews.failed} failed`, `Execution failures: ${reviews.infraFailed}
|
|
153
|
+
lines.push("", `Review rounds: ${reviews.total} total · ${reviews.completed} completed · ${reviews.failed} failed`, `Execution failures: ${reviews.infraFailed}`, ...(reviews.deltaRechecks === 0
|
|
154
154
|
? []
|
|
155
|
-
: [
|
|
156
|
-
`Delta-rechecks: ${reviews.deltaRechecks.total} total · `
|
|
157
|
-
+ `${reviews.deltaRechecks.equivalentAndAccepted} accepted · `
|
|
158
|
-
+ `${reviews.deltaRechecks.finding} finding · `
|
|
159
|
-
+ `${reviews.deltaRechecks.requiresFullReview} requiring Task Agent decision`
|
|
160
|
-
]));
|
|
155
|
+
: [`Delta-rechecks: ${reviews.deltaRechecks} total`]));
|
|
161
156
|
}
|
|
162
157
|
else {
|
|
163
158
|
lines.push("", ...sectionError("reviews", report));
|
|
@@ -224,7 +219,7 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
|
|
|
224
219
|
{ header: "Type", minWidth: 8, maxWidth: 12 },
|
|
225
220
|
{ header: "Turns", minWidth: 4, maxWidth: 6 },
|
|
226
221
|
{ header: "WIs", minWidth: 3, maxWidth: 5 },
|
|
227
|
-
{ header: "Review F/D/
|
|
222
|
+
{ header: "Review F/D/X", minWidth: 12, maxWidth: 16 },
|
|
228
223
|
{ header: "Integration A/F/R", minWidth: 17, maxWidth: 20 },
|
|
229
224
|
{ header: "Pre-progress gen", minWidth: 12, maxWidth: 16 },
|
|
230
225
|
{ header: "Terminal ws", minWidth: 10, maxWidth: 12 },
|
|
@@ -234,7 +229,7 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
|
|
|
234
229
|
task.taskType ?? "unspecified",
|
|
235
230
|
String(task.turns.total),
|
|
236
231
|
String(task.workItems),
|
|
237
|
-
`${task.reviews.full}/${task.reviews.delta}/${task.reviews.
|
|
232
|
+
`${task.reviews.full}/${task.reviews.delta}/${task.reviews.failed}`,
|
|
238
233
|
`${task.integrations.attempts}/${task.integrations.failed}/${task.integrations.repeatedIdentities}`,
|
|
239
234
|
String(task.providerGenerationsBeforeFirstProgress),
|
|
240
235
|
String(task.terminalWorkspaceCount),
|
|
@@ -281,6 +281,12 @@ function unbindRole(args, store) {
|
|
|
281
281
|
assertNoArguments(rest, "Role unbind usage: yui config role unbind <role> <agent-id>");
|
|
282
282
|
store.transaction((tx) => {
|
|
283
283
|
const role = requireRole(name, tx);
|
|
284
|
+
const profileId = profileUsingWorkerBinding(tx, role, agentId);
|
|
285
|
+
if (profileId !== null) {
|
|
286
|
+
throw usageError(`Agent ${agentId} cannot be unbound from Global Role worker because Agent Profile `
|
|
287
|
+
+ `${profileId} derives non-model settings from that binding. `
|
|
288
|
+
+ "Update, inherit, or remove that Profile first.");
|
|
289
|
+
}
|
|
284
290
|
try {
|
|
285
291
|
const result = unbindRoleAgent(role, tx.getGlobalRoleSessionSet(name), agentId, new Date());
|
|
286
292
|
tx.saveGlobalRoleWithSessionSet(result.role, result.sessions);
|
|
@@ -291,6 +297,17 @@ function unbindRole(args, store) {
|
|
|
291
297
|
});
|
|
292
298
|
return `Unbound Agent ${agentId} from role ${name}\n`;
|
|
293
299
|
}
|
|
300
|
+
function profileUsingWorkerBinding(store, role, agentId) {
|
|
301
|
+
if (role.name !== "worker")
|
|
302
|
+
return null;
|
|
303
|
+
if (!Object.hasOwn(role.agentBindings, agentId))
|
|
304
|
+
return null;
|
|
305
|
+
if (agentId === role.activeAgentId)
|
|
306
|
+
return null;
|
|
307
|
+
const profile = store.listAgentProfiles().find((candidate) => (candidate.runtime.source === "explicit"
|
|
308
|
+
&& candidate.runtime.agentId === agentId));
|
|
309
|
+
return profile?.id ?? null;
|
|
310
|
+
}
|
|
294
311
|
function enterRole(args, store) {
|
|
295
312
|
const [rawName, ...rest] = args;
|
|
296
313
|
const name = roleName(rawName);
|
|
@@ -1,14 +1,56 @@
|
|
|
1
1
|
import { usageError } from "../errors/cliError.js";
|
|
2
2
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
3
3
|
import { BUILTIN_PROFILE_IDS, builtinAgentProfileInputs, createAgentProfile, updateAgentProfile } from "../profile/agentProfile.js";
|
|
4
|
+
import { resolveAgentProfileRuntime, resolveAgentProfileView } from "../profile/agentProfileRuntime.js";
|
|
4
5
|
import { parseRoleOptions } from "./roleConfiguration.js";
|
|
5
|
-
|
|
6
|
+
/** Build the exact explicit Profile runtime that a command would persist. */
|
|
7
|
+
export function previewProfileAgentConfigurationMutation(args, store) {
|
|
8
|
+
const [command, ...rest] = args;
|
|
9
|
+
let profile;
|
|
10
|
+
if (command === "add") {
|
|
11
|
+
const usage = "Profile add usage: yui config profile add <id> [--access <read|write>] [Profile settings].";
|
|
12
|
+
const [id, ...tail] = rest;
|
|
13
|
+
if (id === undefined || id.startsWith("--"))
|
|
14
|
+
throw usageError("Profile id is required.", usage);
|
|
15
|
+
const parsed = parseProfileOptions(tail, false, usage);
|
|
16
|
+
assertOptionPairs(parsed);
|
|
17
|
+
const runtime = addedRuntime(parsed, usage);
|
|
18
|
+
if (runtime.source === "global-worker")
|
|
19
|
+
return undefined;
|
|
20
|
+
profile = createAgentProfile({ id, runtime }, new Date(0));
|
|
21
|
+
}
|
|
22
|
+
else if (command === "update") {
|
|
23
|
+
const usage = "Profile update usage: yui config profile update <id> [--access <read|write>] [Profile settings].";
|
|
24
|
+
const [id, ...tail] = rest;
|
|
25
|
+
if (id === undefined || id.startsWith("--"))
|
|
26
|
+
throw usageError("Profile id is required.", usage);
|
|
27
|
+
const parsed = parseProfileOptions(tail, true, usage);
|
|
28
|
+
assertOptionPairs(parsed);
|
|
29
|
+
const current = requireProfile(store, id);
|
|
30
|
+
const runtime = updatedRuntime(current, parsed, usage);
|
|
31
|
+
if (runtime === undefined || runtime.source === "global-worker")
|
|
32
|
+
return undefined;
|
|
33
|
+
profile = updateAgentProfile(current, { runtime }, new Date(Math.max(Date.parse(current.updatedAt) + 1, 1)));
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const runtime = resolveAgentProfileRuntime(profile, store);
|
|
39
|
+
if (runtime.status === "unavailable")
|
|
40
|
+
throw usageError(runtime.reason);
|
|
41
|
+
return {
|
|
42
|
+
agentId: runtime.binding.agentId,
|
|
43
|
+
config: runtime.binding.config,
|
|
44
|
+
cwd: store.getConfig().defaultWorkspace ?? process.cwd()
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function runProfileCommand(args, store, now = () => new Date(), options = {}) {
|
|
6
48
|
const [command, ...rest] = args;
|
|
7
49
|
switch (command) {
|
|
8
|
-
case "add": return addProfile(rest, store, now());
|
|
50
|
+
case "add": return addProfile(rest, store, now(), options);
|
|
9
51
|
case "list": return listProfiles(rest, store);
|
|
10
52
|
case "show": return showProfile(rest, store);
|
|
11
|
-
case "update": return updateProfile(rest, store, now());
|
|
53
|
+
case "update": return updateProfile(rest, store, now(), options);
|
|
12
54
|
case "remove": return removeProfile(rest, store);
|
|
13
55
|
case "reset": return resetProfiles(rest, store, now());
|
|
14
56
|
default:
|
|
@@ -17,45 +59,53 @@ export function runProfileCommand(args, store, now = () => new Date()) {
|
|
|
17
59
|
: `Unknown command: config profile ${command}`);
|
|
18
60
|
}
|
|
19
61
|
}
|
|
20
|
-
function addProfile(args, store, now) {
|
|
62
|
+
function addProfile(args, store, now, options) {
|
|
21
63
|
const usage = "Profile add usage: yui config profile add <id> [--access <read|write>] [Profile settings].";
|
|
22
64
|
const [id, ...tail] = args;
|
|
23
65
|
if (id === undefined || id.startsWith("--"))
|
|
24
66
|
throw usageError("Profile id is required.", usage);
|
|
25
67
|
const parsed = parseProfileOptions(tail, false, usage);
|
|
68
|
+
assertOptionPairs(parsed);
|
|
26
69
|
const profile = createAgentProfile({
|
|
27
70
|
id,
|
|
28
71
|
defaultAccess: parseAccess(parsed.one("--access") ?? "read"),
|
|
29
|
-
...profileValues(parsed)
|
|
72
|
+
...profileValues(parsed),
|
|
73
|
+
runtime: addedRuntime(parsed, usage)
|
|
30
74
|
}, now);
|
|
75
|
+
validateExplicitRuntime(profile, store, options);
|
|
31
76
|
store.transaction((tx) => {
|
|
32
77
|
if (tx.getAgentProfile(profile.id) !== null) {
|
|
33
78
|
throw usageError(`Agent Profile already exists: ${profile.id}.`);
|
|
34
79
|
}
|
|
35
80
|
tx.saveAgentProfile(profile);
|
|
36
81
|
});
|
|
37
|
-
return {
|
|
82
|
+
return {
|
|
83
|
+
output: `Added Agent Profile ${profile.id}\n`,
|
|
84
|
+
data: resolveAgentProfileView(profile, store)
|
|
85
|
+
};
|
|
38
86
|
}
|
|
39
87
|
function listProfiles(args, store) {
|
|
40
88
|
noArgs(args, "Profile list usage: yui config profile list.");
|
|
41
|
-
const profiles = store.listAgentProfiles();
|
|
89
|
+
const profiles = store.listAgentProfiles().map((profile) => resolveAgentProfileView(profile, store));
|
|
42
90
|
const output = profiles.length === 0
|
|
43
91
|
? "No Agent Profiles found.\n"
|
|
44
92
|
: `${renderTable("Agent Profiles", [
|
|
45
93
|
{ header: "Profile", minWidth: 7, maxWidth: 24 },
|
|
46
94
|
{ header: "Revision", minWidth: 8, maxWidth: 10 },
|
|
47
95
|
{ header: "Access", minWidth: 6, maxWidth: 8 },
|
|
48
|
-
{ header: "
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
]), defaultTableWidth())}\n`;
|
|
96
|
+
{ header: "Runtime", minWidth: 9, maxWidth: 14 },
|
|
97
|
+
{ header: "Worker r", minWidth: 8, maxWidth: 10 },
|
|
98
|
+
{ header: "Agent", minWidth: 7, maxWidth: 18 },
|
|
99
|
+
{ header: "Model", minWidth: 7, maxWidth: 24 },
|
|
100
|
+
{ header: "Effort", minWidth: 7, maxWidth: 12 },
|
|
101
|
+
{ header: "Description", minWidth: 12, maxWidth: 48 }
|
|
102
|
+
], profiles.map(profileListRow), defaultTableWidth())}\n`;
|
|
55
103
|
return { output, data: { profiles } };
|
|
56
104
|
}
|
|
57
105
|
function showProfile(args, store) {
|
|
58
106
|
const profile = requireProfile(store, oneArg(args, "Profile show usage: yui config profile show <id>."));
|
|
107
|
+
const view = resolveAgentProfileView(profile, store);
|
|
108
|
+
const runtime = view.runtime;
|
|
59
109
|
return {
|
|
60
110
|
output: `${[
|
|
61
111
|
`Agent Profile: ${profile.id}`,
|
|
@@ -64,13 +114,23 @@ function showProfile(args, store) {
|
|
|
64
114
|
`Description: ${profile.description ?? "-"}`,
|
|
65
115
|
`Instructions: ${profile.instructions ?? "-"}`,
|
|
66
116
|
`Skills: ${profile.skills?.join(", ") || "-"}`,
|
|
67
|
-
`
|
|
68
|
-
`
|
|
117
|
+
`Runtime source: ${runtime.source}`,
|
|
118
|
+
`Worker revision: ${runtime.source === "global-worker"
|
|
119
|
+
? runtime.workerRevision ?? "unavailable"
|
|
120
|
+
: "-"}`,
|
|
121
|
+
`Runtime status: ${runtime.status}`,
|
|
122
|
+
...(runtime.status === "resolved"
|
|
123
|
+
? [
|
|
124
|
+
`Effective Agent: ${runtime.binding.agentId}/${runtime.binding.adapterId}`,
|
|
125
|
+
`Effective Model: ${runtime.binding.config.model ?? "CLI default"}`,
|
|
126
|
+
`Effective Effort: ${runtime.binding.config.effort ?? "CLI default"}`
|
|
127
|
+
]
|
|
128
|
+
: [`Runtime unavailable: ${runtime.reason}`])
|
|
69
129
|
].join("\n")}\n`,
|
|
70
|
-
data:
|
|
130
|
+
data: view
|
|
71
131
|
};
|
|
72
132
|
}
|
|
73
|
-
function updateProfile(args, store, now) {
|
|
133
|
+
function updateProfile(args, store, now, options) {
|
|
74
134
|
const usage = "Profile update usage: yui config profile update <id> [--access <read|write>] [Profile settings].";
|
|
75
135
|
const [id, ...tail] = args;
|
|
76
136
|
if (id === undefined || id.startsWith("--"))
|
|
@@ -80,14 +140,19 @@ function updateProfile(args, store, now) {
|
|
|
80
140
|
throw usageError("At least one Profile option is required.", usage);
|
|
81
141
|
assertOptionPairs(parsed);
|
|
82
142
|
const current = requireProfile(store, id);
|
|
143
|
+
const runtime = updatedRuntime(current, parsed, usage);
|
|
83
144
|
const updated = updateAgentProfile(current, {
|
|
84
145
|
...(parsed.has("--access") ? { defaultAccess: parseAccess(parsed.one("--access")) } : {}),
|
|
85
|
-
...profilePatch(parsed)
|
|
146
|
+
...profilePatch(parsed),
|
|
147
|
+
...(runtime === undefined ? {} : { runtime })
|
|
86
148
|
}, now);
|
|
149
|
+
if (runtime !== undefined) {
|
|
150
|
+
validateExplicitRuntime(updated, store, options);
|
|
151
|
+
}
|
|
87
152
|
store.saveAgentProfile(updated);
|
|
88
153
|
return {
|
|
89
154
|
output: `Updated Agent Profile ${updated.id} to revision ${updated.revision}\n`,
|
|
90
|
-
data:
|
|
155
|
+
data: resolveAgentProfileView(updated, store)
|
|
91
156
|
};
|
|
92
157
|
}
|
|
93
158
|
function removeProfile(args, store) {
|
|
@@ -111,12 +176,11 @@ function resetProfiles(args, store, now) {
|
|
|
111
176
|
description: desired.description,
|
|
112
177
|
instructions: desired.instructions,
|
|
113
178
|
skills: desired.skills,
|
|
114
|
-
|
|
115
|
-
effort: desired.effort
|
|
179
|
+
runtime: desired.runtime
|
|
116
180
|
}, now));
|
|
117
181
|
}
|
|
118
182
|
});
|
|
119
|
-
const profiles = BUILTIN_PROFILE_IDS.map((id) => requireProfile(store, id));
|
|
183
|
+
const profiles = BUILTIN_PROFILE_IDS.map((id) => resolveAgentProfileView(requireProfile(store, id), store));
|
|
120
184
|
return {
|
|
121
185
|
output: `Reset ${BUILTIN_PROFILE_IDS.length} built-in Agent Profiles\n`,
|
|
122
186
|
data: { profiles }
|
|
@@ -127,8 +191,10 @@ const BASE_OPTIONS = [
|
|
|
127
191
|
["--description", "value"],
|
|
128
192
|
["--instructions", "value"],
|
|
129
193
|
["--skill", "repeatable"],
|
|
194
|
+
["--agent", "value"],
|
|
130
195
|
["--model", "value"],
|
|
131
|
-
["--effort", "value"]
|
|
196
|
+
["--effort", "value"],
|
|
197
|
+
["--inherit-worker", "flag"]
|
|
132
198
|
];
|
|
133
199
|
const CLEAR_OPTIONS = [
|
|
134
200
|
["--clear-description", "flag"],
|
|
@@ -147,9 +213,7 @@ function profileValues(parsed) {
|
|
|
147
213
|
return {
|
|
148
214
|
...(parsed.has("--description") ? { description: requiredText(parsed.one("--description")) } : {}),
|
|
149
215
|
...(parsed.has("--instructions") ? { instructions: requiredText(parsed.one("--instructions")) } : {}),
|
|
150
|
-
...(parsed.has("--skill") ? { skills: parsed.many("--skill").map(requiredText) } : {})
|
|
151
|
-
...(parsed.has("--model") ? { model: requiredText(parsed.one("--model")) } : {}),
|
|
152
|
-
...(parsed.has("--effort") ? { effort: requiredText(parsed.one("--effort")) } : {})
|
|
216
|
+
...(parsed.has("--skill") ? { skills: parsed.many("--skill").map(requiredText) } : {})
|
|
153
217
|
};
|
|
154
218
|
}
|
|
155
219
|
function profilePatch(parsed) {
|
|
@@ -157,11 +221,117 @@ function profilePatch(parsed) {
|
|
|
157
221
|
...profileValues(parsed),
|
|
158
222
|
...(parsed.has("--clear-description") ? { description: undefined } : {}),
|
|
159
223
|
...(parsed.has("--clear-instructions") ? { instructions: undefined } : {}),
|
|
160
|
-
...(parsed.has("--clear-skills") ? { skills: undefined } : {})
|
|
161
|
-
...(parsed.has("--clear-model") ? { model: undefined } : {}),
|
|
162
|
-
...(parsed.has("--clear-effort") ? { effort: undefined } : {})
|
|
224
|
+
...(parsed.has("--clear-skills") ? { skills: undefined } : {})
|
|
163
225
|
};
|
|
164
226
|
}
|
|
227
|
+
function addedRuntime(parsed, usage) {
|
|
228
|
+
assertRuntimeSourceOptions(parsed, usage);
|
|
229
|
+
const agentId = parsed.one("--agent")?.trim();
|
|
230
|
+
if (parsed.has("--inherit-worker") || agentId === undefined) {
|
|
231
|
+
if (parsed.has("--model") || parsed.has("--effort")) {
|
|
232
|
+
throw usageError("--model and --effort require --agent for an explicit Profile.", usage);
|
|
233
|
+
}
|
|
234
|
+
return { source: "global-worker" };
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
source: "explicit",
|
|
238
|
+
agentId: requiredText(agentId),
|
|
239
|
+
...(parsed.has("--model") ? { model: requiredText(parsed.one("--model")) } : {}),
|
|
240
|
+
...(parsed.has("--effort") ? { effort: requiredText(parsed.one("--effort")) } : {})
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function updatedRuntime(profile, parsed, usage) {
|
|
244
|
+
assertRuntimeSourceOptions(parsed, usage);
|
|
245
|
+
if (parsed.has("--inherit-worker"))
|
|
246
|
+
return { source: "global-worker" };
|
|
247
|
+
const runtimeOptions = [
|
|
248
|
+
"--agent", "--model", "--effort", "--clear-model", "--clear-effort"
|
|
249
|
+
].some((option) => parsed.has(option));
|
|
250
|
+
if (!runtimeOptions)
|
|
251
|
+
return undefined;
|
|
252
|
+
const agentId = parsed.one("--agent")?.trim();
|
|
253
|
+
if (profile.runtime.source === "global-worker") {
|
|
254
|
+
if (agentId === undefined) {
|
|
255
|
+
throw usageError("An inherited Profile requires --agent before model or effort can be customized.", usage);
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
source: "explicit",
|
|
259
|
+
agentId: requiredText(agentId),
|
|
260
|
+
...(parsed.has("--model") ? { model: requiredText(parsed.one("--model")) } : {}),
|
|
261
|
+
...(parsed.has("--effort") ? { effort: requiredText(parsed.one("--effort")) } : {})
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
const nextAgentId = agentId ?? profile.runtime.agentId;
|
|
265
|
+
const changesAgent = nextAgentId !== profile.runtime.agentId;
|
|
266
|
+
if (changesAgent) {
|
|
267
|
+
for (const [field, setOption, clearOption] of [
|
|
268
|
+
["model", "--model", "--clear-model"],
|
|
269
|
+
["effort", "--effort", "--clear-effort"]
|
|
270
|
+
]) {
|
|
271
|
+
if (profile.runtime[field] !== undefined
|
|
272
|
+
&& !parsed.has(setOption)
|
|
273
|
+
&& !parsed.has(clearOption)) {
|
|
274
|
+
throw usageError(`Changing --agent requires ${setOption} or ${clearOption} so the old ${field} is not reused.`, usage);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
source: "explicit",
|
|
279
|
+
agentId: requiredText(nextAgentId),
|
|
280
|
+
...(parsed.has("--model") ? { model: requiredText(parsed.one("--model")) } : {}),
|
|
281
|
+
...(parsed.has("--effort") ? { effort: requiredText(parsed.one("--effort")) } : {})
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
source: "explicit",
|
|
286
|
+
agentId: profile.runtime.agentId,
|
|
287
|
+
...(parsed.has("--clear-model")
|
|
288
|
+
? {}
|
|
289
|
+
: parsed.has("--model")
|
|
290
|
+
? { model: requiredText(parsed.one("--model")) }
|
|
291
|
+
: profile.runtime.model === undefined ? {} : { model: profile.runtime.model }),
|
|
292
|
+
...(parsed.has("--clear-effort")
|
|
293
|
+
? {}
|
|
294
|
+
: parsed.has("--effort")
|
|
295
|
+
? { effort: requiredText(parsed.one("--effort")) }
|
|
296
|
+
: profile.runtime.effort === undefined ? {} : { effort: profile.runtime.effort })
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function assertRuntimeSourceOptions(parsed, usage) {
|
|
300
|
+
if (!parsed.has("--inherit-worker"))
|
|
301
|
+
return;
|
|
302
|
+
if (["--agent", "--model", "--effort", "--clear-model", "--clear-effort"]
|
|
303
|
+
.some((option) => parsed.has(option))) {
|
|
304
|
+
throw usageError("--inherit-worker cannot be combined with explicit Agent, model, or effort options.", usage);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
function validateExplicitRuntime(profile, store, options) {
|
|
308
|
+
if (profile.runtime.source !== "explicit")
|
|
309
|
+
return;
|
|
310
|
+
const runtime = resolveAgentProfileRuntime(profile, store);
|
|
311
|
+
if (runtime.status === "unavailable")
|
|
312
|
+
throw usageError(runtime.reason);
|
|
313
|
+
options.validateAgentConfiguration?.({
|
|
314
|
+
agentId: runtime.binding.agentId,
|
|
315
|
+
config: runtime.binding.config,
|
|
316
|
+
cwd: store.getConfig().defaultWorkspace ?? process.cwd()
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
function profileListRow(view) {
|
|
320
|
+
const { profile, runtime } = view;
|
|
321
|
+
return [
|
|
322
|
+
profile.id,
|
|
323
|
+
String(profile.revision),
|
|
324
|
+
profile.defaultAccess,
|
|
325
|
+
runtime.source,
|
|
326
|
+
runtime.source === "global-worker"
|
|
327
|
+
? runtime.workerRevision === undefined ? "-" : String(runtime.workerRevision)
|
|
328
|
+
: "-",
|
|
329
|
+
runtime.status === "resolved" ? runtime.binding.agentId : "unavailable",
|
|
330
|
+
runtime.status === "resolved" ? runtime.binding.config.model ?? "CLI default" : "-",
|
|
331
|
+
runtime.status === "resolved" ? runtime.binding.config.effort ?? "CLI default" : "-",
|
|
332
|
+
profile.description ?? "-"
|
|
333
|
+
];
|
|
334
|
+
}
|
|
165
335
|
function assertOptionPairs(parsed) {
|
|
166
336
|
for (const [value, clear] of [
|
|
167
337
|
["--description", "--clear-description"],
|
|
@@ -104,7 +104,7 @@ function sameStopCandidate(session, candidate) {
|
|
|
104
104
|
return session.agentId === candidate.agentId
|
|
105
105
|
&& session.adapterId === candidate.adapterId
|
|
106
106
|
&& session.nativeSessionId === candidate.nativeSessionId
|
|
107
|
-
&& session.
|
|
107
|
+
&& session.runtimeGenerationId === candidate.runtimeGenerationId
|
|
108
108
|
&& session.sessionUpdatedAt === candidate.sessionUpdatedAt;
|
|
109
109
|
}
|
|
110
110
|
function renderSessionOwner(session) {
|