@zq-silk/yui 2.15.0 → 2.17.0
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/dist/cli/commandCatalog.js +17 -11
- package/dist/cli/interactionCandidates.js +20 -5
- package/dist/cli/interactionPolicy.js +5 -4
- package/dist/cli/roleWizard.js +7 -0
- package/dist/commands/globalRoleCommands.js +13 -2
- package/dist/commands/operatorCommands.js +2 -0
- package/dist/commands/profileCommands.js +19 -2
- package/dist/commands/roleConfiguration.js +3 -0
- package/dist/commands/taskCommands.js +16 -0
- package/dist/commands/taskRoleRuntimeStatus.js +3 -0
- package/dist/controller/agentJobControl.js +9 -1
- package/dist/controller/fileSchedulerStoreAdapter.js +35 -4
- package/dist/controller/globalInputDelivery.js +14 -8
- package/dist/controller/providerRetryAdmission.js +9 -0
- package/dist/controller/providerRetryDelivery.js +16 -0
- package/dist/controller/runtime.js +14 -0
- package/dist/controller/runtimeLaunchCoordinator.js +3 -1
- package/dist/executor/fileRoleLaunchPlanner.js +3 -0
- package/dist/message/messageContinuation.js +4 -0
- package/dist/output/rolePresentation.js +3 -0
- package/dist/profile/agentProfile.js +8 -0
- package/dist/profile/agentProfileRuntime.js +16 -1
- package/dist/review/reviewDecision.js +6 -0
- package/dist/role/availability.js +28 -0
- package/dist/role/role.js +6 -0
- package/dist/runtime/codexNativeAccess.js +21 -4
- package/dist/runtime/tmuxAdapters.js +5 -0
- package/dist/scheduler/activeRoleRunDelivery.js +9 -0
- package/dist/scheduler/leaderWakeupProcessor.js +8 -1
- package/dist/scheduler/roleRunLiveness.js +3 -0
- package/dist/storage/sqliteSchema.js +10 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/web/assets/client/app/catalog.js +40 -14
- package/dist/web/assets/client/app/main.js +43 -26
- package/dist/web/assets/client/app/manage.js +18 -0
- package/dist/web/assets/client/app/selection.js +4 -1
- package/dist/web/assets/client/app/taskView.js +29 -16
- package/dist/web/assets/client/i18n/en.js +72 -21
- package/dist/web/assets/client/i18n/zh-CN.js +72 -21
- package/dist/web/assets/client/index.js +6 -0
- package/dist/web/assets/client/layout/manageShell.js +30 -8
- package/dist/web/assets/client/layout/shortcuts.js +9 -5
- package/dist/web/assets/client/layout/sidebarNav.js +147 -0
- package/dist/web/assets/client/layout/sizing.js +5 -2
- package/dist/web/assets/client/layout/workspace.js +193 -107
- package/dist/web/assets/client/lib/motion.js +59 -0
- package/dist/web/assets/client/lib/prefs.js +29 -2
- package/dist/web/assets/client/lib/theme.js +11 -4
- package/dist/web/assets/client/ui/primitives.js +40 -0
- package/dist/web/assets/client/views/dock/conversation.js +421 -123
- package/dist/web/assets/client/views/dock/session.js +229 -0
- package/dist/web/assets/client/views/dock/terminal.js +48 -39
- package/dist/web/assets/client/views/overview.js +8 -5
- package/dist/web/assets/client/views/task/page.js +7 -17
- package/dist/web/assets/shared/brand.js +22 -0
- package/dist/web/assets/shared/geometry.js +7 -1
- package/dist/web/assets/shared/icons.js +4 -0
- package/dist/web/assets/shared/motion.js +74 -0
- package/dist/web/assets/shell/appRail.js +14 -0
- package/dist/web/assets/shell/dialogs.js +1 -0
- package/dist/web/assets/shell/dock.js +19 -24
- package/dist/web/assets/shell/index.js +6 -2
- package/dist/web/assets/shell/login.js +9 -8
- package/dist/web/assets/shell/manage.js +32 -20
- package/dist/web/assets/shell/settingsContent.js +1 -1
- package/dist/web/assets/shell/sidebar.js +16 -23
- package/dist/web/assets/styles/base.js +1 -1
- package/dist/web/assets/styles/index.js +2 -0
- package/dist/web/assets/styles/layout/responsive.js +63 -19
- package/dist/web/assets/styles/layout/workspace.js +53 -16
- package/dist/web/assets/styles/motion.js +51 -0
- package/dist/web/assets/styles/tokens.js +5 -3
- package/dist/web/assets/styles/ui/controls.js +1 -1
- package/dist/web/assets/styles/ui/forms.js +1 -1
- package/dist/web/assets/styles/ui/surfaces.js +1 -2
- package/dist/web/assets/styles/views/conversation.js +97 -18
- package/dist/web/assets/styles/views/dock.js +34 -2
- package/dist/web/assets/styles/views/login.js +16 -13
- package/dist/web/assets/styles/views/manage.js +25 -42
- package/dist/web/assets/styles/views/page.js +2 -3
- package/dist/web/assets/styles/views/records.js +1 -1
- package/dist/web/assets/styles/views/settings.js +1 -1
- package/dist/web/assets/styles/views/sidebar.js +25 -20
- package/dist/web/assets/styles/views/task.js +8 -15
- package/dist/web/assets/styles/views/terminal.js +13 -9
- package/dist/web/webConversation.js +1 -0
- package/dist/web/webSettings.js +10 -2
- package/docs/managed-turn-and-session-runtime.md +10 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +6 -0
- package/docs/release-workflow.md +46 -0
- package/docs/release-workflow.zh-CN.md +34 -0
- package/docs/roles-and-configuration.md +45 -1
- package/docs/roles-and-configuration.zh-CN.md +35 -0
- package/docs/storage-baseline.md +6 -1
- package/docs/storage-baseline.zh-CN.md +5 -1
- package/docs/testing/review-evaluation.md +35 -1
- package/docs/testing/review-evaluation.zh-CN.md +25 -1
- package/docs/testing/verification-levels.md +2 -2
- package/docs/testing/verification-levels.zh-CN.md +2 -2
- package/package.json +1 -1
- package/skills/yui-leader/VERSION +1 -1
- package/skills/yui-leader/references/execution.md +19 -8
- package/skills/yui-reviewer/SKILL.md +13 -2
- package/skills/yui-reviewer/VERSION +1 -1
|
@@ -176,13 +176,15 @@ const roleChildren = [
|
|
|
176
176
|
},
|
|
177
177
|
{ name: "list", discovery: { surface: "public", audiences: ["public", "operator", "leader", "assignment", "global"] }, summary: "List global Roles." },
|
|
178
178
|
{ name: "show", discovery: { surface: "public", audiences: ["public", "operator", "leader", "assignment", "global"] }, summary: "Show one global Role.", usage: "yui config role show <name>" },
|
|
179
|
+
{ name: "enable", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Enable new execution and template selection; does not start a Session.", usage: "yui config role enable <name>" },
|
|
180
|
+
{ name: "disable", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Disable new execution and template selection; keeps running Sessions and input.", usage: "yui config role disable <name>" },
|
|
179
181
|
{
|
|
180
182
|
name: "update", discovery: { surface: "public", audiences: ["public", "operator"] },
|
|
181
183
|
summary: "Update a global Role.",
|
|
182
184
|
usage: "yui config role update <name> [profile options] [clear options]",
|
|
183
|
-
options: ["--agent", "--workspace", ...roleProfileOptions, ...roleAgentOptions,
|
|
185
|
+
options: ["--enabled", "--agent", "--workspace", ...roleProfileOptions, ...roleAgentOptions,
|
|
184
186
|
...roleProfileClearOptions, ...roleAgentClearOptions, "--yes"],
|
|
185
|
-
optionValues: roleAgentOptionValues,
|
|
187
|
+
optionValues: { ...roleAgentOptionValues, "--enabled": ["true", "false"] },
|
|
186
188
|
fileOptions: ["--workspace"]
|
|
187
189
|
},
|
|
188
190
|
{ name: "remove", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Remove a global Role.", usage: "yui config role remove <name>" },
|
|
@@ -238,17 +240,19 @@ const profileChildren = [
|
|
|
238
240
|
name: "add", discovery: { surface: "public", audiences: ["public", "operator"] },
|
|
239
241
|
summary: "Add a reusable Agent Profile that inherits Worker runtime or selects one explicit Agent.",
|
|
240
242
|
usage: "yui config profile add <id> [--access <read|write>] [--inherit-worker | --agent <id> [--model <model>] [--effort <effort>]] [Profile settings]",
|
|
241
|
-
options: ["--access", ...agentProfileOptions],
|
|
242
|
-
optionValues: { "--access": ["read", "write"] }
|
|
243
|
+
options: ["--enabled", "--access", ...agentProfileOptions],
|
|
244
|
+
optionValues: { "--enabled": ["true", "false"], "--access": ["read", "write"] }
|
|
243
245
|
},
|
|
244
246
|
{ name: "list", discovery: { surface: "public", audiences: ["public", "operator", "leader", "assignment", "global"] }, summary: "List Agent Profiles." },
|
|
245
247
|
{ name: "show", discovery: { surface: "public", audiences: ["public", "operator", "leader", "assignment", "global"] }, summary: "Show one Agent Profile.", usage: "yui config profile show <id>" },
|
|
248
|
+
{ name: "enable", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Enable new Profile applications; existing Task bindings are unchanged.", usage: "yui config profile enable <id>" },
|
|
249
|
+
{ name: "disable", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Disable new Profile applications without deleting configuration.", usage: "yui config profile disable <id>" },
|
|
246
250
|
{
|
|
247
251
|
name: "update", discovery: { surface: "public", audiences: ["public", "operator"] },
|
|
248
252
|
summary: "Update an Agent Profile's behavior or runtime source.",
|
|
249
253
|
usage: "yui config profile update <id> [--access <read|write>] [--inherit-worker | [--agent <id>] [--model <model>|--clear-model] [--effort <effort>|--clear-effort]] [Profile settings]",
|
|
250
|
-
options: ["--access", ...agentProfileOptions, ...agentProfileClearOptions],
|
|
251
|
-
optionValues: { "--access": ["read", "write"] }
|
|
254
|
+
options: ["--enabled", "--access", ...agentProfileOptions, ...agentProfileClearOptions],
|
|
255
|
+
optionValues: { "--enabled": ["true", "false"], "--access": ["read", "write"] }
|
|
252
256
|
},
|
|
253
257
|
{ name: "remove", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Remove a custom Agent Profile.", usage: "yui config profile remove <id>" },
|
|
254
258
|
{ name: "reset", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Reset all built-in Agent Profiles." }
|
|
@@ -688,7 +692,7 @@ const taskChildren = [
|
|
|
688
692
|
name: "role", discovery: { surface: "managed", audiences: ["operator", "leader"] },
|
|
689
693
|
summary: "Manage Roles within a Task.",
|
|
690
694
|
sections: [{ id: "manage", title: "Commands", entries: [
|
|
691
|
-
"add", "list", "status", "show", "update", "remove", "bind", "unbind",
|
|
695
|
+
"add", "list", "status", "show", "update", "enable", "disable", "remove", "bind", "unbind",
|
|
692
696
|
"capabilities", "session", "interrupt", "view", "takeover", "release"
|
|
693
697
|
] }],
|
|
694
698
|
children: [
|
|
@@ -707,6 +711,8 @@ const taskChildren = [
|
|
|
707
711
|
options: ["--cursor"]
|
|
708
712
|
},
|
|
709
713
|
{ name: "show", discovery: { surface: "managed", audiences: ["operator", "leader"] }, summary: "Show one Task Role.", usage: "yui task role show <task> <role> [--cursor <cursor>]", options: ["--cursor"] },
|
|
714
|
+
{ name: "enable", discovery: { surface: "managed", audiences: ["operator", "leader"] }, summary: "Enable this Task Role; does not change Task execution authority.", usage: "yui task role enable <task> <role>" },
|
|
715
|
+
{ name: "disable", discovery: { surface: "managed", audiences: ["operator", "leader"] }, summary: "Block new execution of this Task Role without stopping its current Run.", usage: "yui task role disable <task> <role>" },
|
|
710
716
|
{ name: "capabilities", discovery: { surface: "managed", audiences: ["operator", "leader"] }, summary: "Read native options using this Role's desired or exact failed launch configuration.",
|
|
711
717
|
usage: "yui task role capabilities <task> <role> [--error <event-id>] [--refresh]",
|
|
712
718
|
options: ["--error", "--refresh"] },
|
|
@@ -714,9 +720,9 @@ const taskChildren = [
|
|
|
714
720
|
name: "update", discovery: { surface: "managed", audiences: ["operator", "leader"] },
|
|
715
721
|
summary: "Update a Task Role; Agent settings target the named or active binding without switching it.",
|
|
716
722
|
usage: "yui task role update <task> <role> [--profile <id>] [--agent <id>] [--environment <preparation-id> | --managed-environment] [Role and Agent settings]",
|
|
717
|
-
options: ["--profile", "--agent", "--environment", "--managed-environment", ...roleProfileOptions, ...roleAgentOptions,
|
|
723
|
+
options: ["--enabled", "--profile", "--agent", "--environment", "--managed-environment", ...roleProfileOptions, ...roleAgentOptions,
|
|
718
724
|
...roleProfileClearOptions, ...roleAgentClearOptions, "--yes"],
|
|
719
|
-
optionValues: roleAgentOptionValues
|
|
725
|
+
optionValues: { ...roleAgentOptionValues, "--enabled": ["true", "false"] }
|
|
720
726
|
},
|
|
721
727
|
{ name: "remove", discovery: { surface: "managed", audiences: ["operator", "leader"] }, summary: "Remove a Task Role.", usage: "yui task role remove <task> <role>" },
|
|
722
728
|
{ name: "bind", discovery: { surface: "managed", audiences: ["operator", "leader"] }, summary: "Bind and activate an Agent for a Task Role.", usage: "yui task role bind <task> <role> <agent-id>" },
|
|
@@ -1394,7 +1400,7 @@ export const ROOT_COMMAND = buildNode({
|
|
|
1394
1400
|
examples: ["yui config profile list", "yui config profile show implementer"],
|
|
1395
1401
|
sections: [
|
|
1396
1402
|
{ id: "inspect", title: "Inspect", entries: ["list", "show"] },
|
|
1397
|
-
{ id: "manage", title: "Manage", entries: ["add", "update", "remove", "reset"] }
|
|
1403
|
+
{ id: "manage", title: "Manage", entries: ["add", "update", "enable", "disable", "remove", "reset"] }
|
|
1398
1404
|
],
|
|
1399
1405
|
children: profileChildren
|
|
1400
1406
|
},
|
|
@@ -1404,7 +1410,7 @@ export const ROOT_COMMAND = buildNode({
|
|
|
1404
1410
|
examples: ["yui config role list", "yui config role show operator"],
|
|
1405
1411
|
sections: [
|
|
1406
1412
|
{ id: "inspect", title: "Inspect", entries: ["list", "show"] },
|
|
1407
|
-
{ id: "manage", title: "Manage", entries: ["add", "update", "remove", "bind", "unbind"] }
|
|
1413
|
+
{ id: "manage", title: "Manage", entries: ["add", "update", "enable", "disable", "remove", "bind", "unbind"] }
|
|
1408
1414
|
],
|
|
1409
1415
|
children: roleChildren
|
|
1410
1416
|
},
|
|
@@ -21,9 +21,22 @@ export async function getSelectionCandidates(selector, ports, args) {
|
|
|
21
21
|
return { ...set, defaultValue };
|
|
22
22
|
}
|
|
23
23
|
case "agent-profiles":
|
|
24
|
-
return entities("Agent Profile", "Select Agent Profile", await list(ports, "profile.list", {})
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
return entities("Agent Profile", "Select Agent Profile", (await list(ports, "profile.list", {}))
|
|
25
|
+
.filter(p => selector.actionTarget || p.enabled !== false
|
|
26
|
+
&& p.effectiveRuntime?.status !== "unavailable")
|
|
27
|
+
.map(p => ({ ...p, availability: p.enabled === false ? "disabled" : "enabled" })), ["id", "availability", "revision", "defaultAccess", "agentId"]);
|
|
28
|
+
case "global-roles": {
|
|
29
|
+
let roles = await list(ports, "role.list", {});
|
|
30
|
+
// Review uses a Task-local binding first; a disabled same-named template
|
|
31
|
+
// must not hide an independently enabled existing Reviewer (or vice versa).
|
|
32
|
+
if (args[0] === "task" && args[1] === "review" && args[2] === "request" && args[3] !== undefined) {
|
|
33
|
+
const local = await list(ports, "task.role.list", { taskId: args[3] });
|
|
34
|
+
roles = [...roles.filter(r => !local.some(t => t.name === r.name)), ...local];
|
|
35
|
+
}
|
|
36
|
+
return entities("global role", "Select global Role", roles
|
|
37
|
+
.filter(r => selector.actionTarget && !(args[0] === "session" && args[1] === "enter") || r.enabled !== false)
|
|
38
|
+
.map(r => ({ ...r, availability: r.enabled === false ? "disabled" : "enabled" })), ["name", "availability", "activeAgentId", "workspace"]);
|
|
39
|
+
}
|
|
27
40
|
case "jobs":
|
|
28
41
|
return entities("job", "Select job", await list(ports, "jobs.list", {}), ["id", "type", "status"]);
|
|
29
42
|
case "input-requests": {
|
|
@@ -41,9 +54,11 @@ export async function getSelectionCandidates(selector, ports, args) {
|
|
|
41
54
|
const taskId = dependencyValue(selector, args);
|
|
42
55
|
if (taskId === undefined)
|
|
43
56
|
return null;
|
|
44
|
-
const roles = orderRoleOptions(await list(ports, "task.role.list", { taskId }))
|
|
57
|
+
const roles = orderRoleOptions((await list(ports, "task.role.list", { taskId }))
|
|
58
|
+
.filter(r => selector.actionTarget || r.enabled !== false))
|
|
59
|
+
.map(r => ({ ...r, availability: r.enabled === false ? "disabled" : "enabled" }));
|
|
45
60
|
return entities("task role", `Select Task role: ${taskId}`, roles, [
|
|
46
|
-
"name", "kind", "activeAgentId"
|
|
61
|
+
"name", "availability", "kind", "activeAgentId"
|
|
47
62
|
]);
|
|
48
63
|
}
|
|
49
64
|
case "task-decisions": {
|
|
@@ -92,7 +92,7 @@ export const INTERACTION_POLICIES = Object.freeze([
|
|
|
92
92
|
"--search": "value"
|
|
93
93
|
}
|
|
94
94
|
},
|
|
95
|
-
...["show", "update", "remove", "enter", "context"].map((command) => ({
|
|
95
|
+
...["show", "update", "remove", "enable", "disable", "enter", "context"].map((command) => ({
|
|
96
96
|
commandPath: command === "enter" || command === "context"
|
|
97
97
|
? ["session", command]
|
|
98
98
|
: ["config", "role", command],
|
|
@@ -129,12 +129,13 @@ export const INTERACTION_POLICIES = Object.freeze([
|
|
|
129
129
|
commandPath: ["config", "profile", "add"],
|
|
130
130
|
selectors: [],
|
|
131
131
|
trailingOptions: {
|
|
132
|
+
"--enabled": "value",
|
|
132
133
|
"--description": "value", "--instructions": "value",
|
|
133
134
|
"--skill": "value", "--agent": "value", "--model": "value",
|
|
134
135
|
"--effort": "value", "--access": "value", "--inherit-worker": "flag"
|
|
135
136
|
}
|
|
136
137
|
},
|
|
137
|
-
...["show", "update", "remove"].map((command) => ({
|
|
138
|
+
...["show", "update", "remove", "enable", "disable"].map((command) => ({
|
|
138
139
|
commandPath: ["config", "profile", command],
|
|
139
140
|
selectors: [{
|
|
140
141
|
argumentIndex: 3,
|
|
@@ -145,7 +146,7 @@ export const INTERACTION_POLICIES = Object.freeze([
|
|
|
145
146
|
...(command === "update"
|
|
146
147
|
? {
|
|
147
148
|
trailingOptions: {
|
|
148
|
-
"--description": "option-like-value",
|
|
149
|
+
"--enabled": "value", "--description": "option-like-value",
|
|
149
150
|
"--instructions": "option-like-value", "--skill": "value",
|
|
150
151
|
"--agent": "value", "--inherit-worker": "flag",
|
|
151
152
|
"--model": "option-like-value", "--effort": "option-like-value",
|
|
@@ -380,7 +381,7 @@ export const INTERACTION_POLICIES = Object.freeze([
|
|
|
380
381
|
commandPath: ["task", "role", "list"],
|
|
381
382
|
selectors: [{ argumentIndex: 3, entity: "task", provider: "tasks", actionTarget: true }]
|
|
382
383
|
},
|
|
383
|
-
...["show", "status", "update", "remove"].map((command) => ({
|
|
384
|
+
...["show", "status", "update", "remove", "enable", "disable"].map((command) => ({
|
|
384
385
|
commandPath: ["task", "role", command],
|
|
385
386
|
selectors: [
|
|
386
387
|
{ argumentIndex: 3, entity: "task", provider: "tasks", actionTarget: true },
|
package/dist/cli/roleWizard.js
CHANGED
|
@@ -281,6 +281,7 @@ async function settingsSection(title, io, taskRole) {
|
|
|
281
281
|
}
|
|
282
282
|
async function updateRoleSettings(args, role, ports, io, taskRole) {
|
|
283
283
|
const fields = [
|
|
284
|
+
{ value: "enabled", cells: ["Enabled (not Session state)", role.enabled === false ? "false" : "true"] },
|
|
284
285
|
profileChoice("description", "Description", role.description),
|
|
285
286
|
profileChoice("responsibilities", "Responsibilities", role.responsibilities),
|
|
286
287
|
profileChoice("constraints", "Constraints", role.constraints),
|
|
@@ -292,6 +293,11 @@ async function updateRoleSettings(args, role, ports, io, taskRole) {
|
|
|
292
293
|
const field = await choose("Role settings", fields, [TEXT_COLUMN, { header: "Current", minWidth: 12, maxWidth: 52 }], io, undefined, "field");
|
|
293
294
|
if (field === undefined)
|
|
294
295
|
return { kind: "cancelled", args };
|
|
296
|
+
if (field === "enabled") {
|
|
297
|
+
const value = await choose("Allow new execution; running Sessions are not stopped", [{ value: "true", cells: ["Enabled"] }, { value: "false", cells: ["Disabled"] }], [TEXT_COLUMN], io, role.enabled === false ? "false" : "true", "enabled");
|
|
298
|
+
return value === undefined ? { kind: "cancelled", args }
|
|
299
|
+
: { kind: "resolved", args: [...args, "--enabled", value] };
|
|
300
|
+
}
|
|
295
301
|
if (field === "active-agent") {
|
|
296
302
|
const target = await selectActiveAgent(role, ports, io);
|
|
297
303
|
if (target === undefined)
|
|
@@ -672,6 +678,7 @@ function asRole(value) {
|
|
|
672
678
|
}));
|
|
673
679
|
return {
|
|
674
680
|
name,
|
|
681
|
+
enabled: input.enabled !== false,
|
|
675
682
|
activeAgentId,
|
|
676
683
|
agentBindings,
|
|
677
684
|
...(stringField(input, "workspace") === undefined ? {} : { workspace: stringField(input, "workspace") }),
|
|
@@ -10,6 +10,7 @@ import { inboxEntryCategory } from "../message/inboxTurn.js";
|
|
|
10
10
|
import { checkCurrentSkills, SKILL_REUSE_RULE } from "../context/currentSkills.js";
|
|
11
11
|
import { runtimeDiagnosticGroups, runtimeDiagnosticEvidence } from "../controller/globalRuntimeAttention.js";
|
|
12
12
|
import { roleSkillIds } from "../context/roleSessionContext.js";
|
|
13
|
+
import { isEnabled } from "../role/availability.js";
|
|
13
14
|
import { createRoleSessionSet, recordRoleAgentSession } from "../executor/agentExecutor.js";
|
|
14
15
|
import { resolveEffectiveLaunch } from "../executor/effectiveLaunch.js";
|
|
15
16
|
import { managedGlobalRoleWorkspace } from "../storage/homeLayout.js";
|
|
@@ -96,6 +97,12 @@ export function runGlobalRoleCommand(args, store, options = {}) {
|
|
|
96
97
|
case "show": return showRole(rest, store);
|
|
97
98
|
case "context": return roleContext(rest, store, options);
|
|
98
99
|
case "update": return updateRole(rest, store, options);
|
|
100
|
+
case "enable":
|
|
101
|
+
case "disable": {
|
|
102
|
+
if (rest.length !== 1)
|
|
103
|
+
throw usageError(`Usage: yui config role ${command} <role>`);
|
|
104
|
+
return updateRole([rest[0], "--enabled", String(command === "enable")], store, options);
|
|
105
|
+
}
|
|
99
106
|
case "remove": return removeRole(rest, store);
|
|
100
107
|
case "bind": return bindRole(rest, store);
|
|
101
108
|
case "unbind": return unbindRole(rest, store);
|
|
@@ -292,7 +299,7 @@ function listRoles(args, store) {
|
|
|
292
299
|
for (const name of SYSTEM_ROLE_NAMES) {
|
|
293
300
|
const role = store.getGlobalRole(name);
|
|
294
301
|
rows.set(name, role === null
|
|
295
|
-
? [name, "system", "?", "?", "?", "?"]
|
|
302
|
+
? [name, "system", "?", "?", "?", "?", "?"]
|
|
296
303
|
: roleListRow(role, "system"));
|
|
297
304
|
}
|
|
298
305
|
for (const role of store.listGlobalRoles()) {
|
|
@@ -305,6 +312,7 @@ function listRoles(args, store) {
|
|
|
305
312
|
return `${renderTable("Roles", [
|
|
306
313
|
{ header: "Role", minWidth: 4, maxWidth: 24 },
|
|
307
314
|
{ header: "Kind", minWidth: 6, maxWidth: 8 },
|
|
315
|
+
{ header: "Enabled", minWidth: 7, maxWidth: 8 },
|
|
308
316
|
{ header: "Active Agent", minWidth: 8, maxWidth: 20 },
|
|
309
317
|
{ header: "Model", minWidth: 8, maxWidth: 22 },
|
|
310
318
|
{ header: "Effort", minWidth: 8, maxWidth: 14 },
|
|
@@ -344,6 +352,9 @@ function updateRole(args, store, options) {
|
|
|
344
352
|
const workspace = trimmed(parsed.one("--workspace"));
|
|
345
353
|
const next = store.transaction((tx) => {
|
|
346
354
|
const role = requireRole(name, tx);
|
|
355
|
+
if (parsed.seen.size === 1 && parsed.has("--enabled")
|
|
356
|
+
&& isEnabled(role) === roleProfilePatch(parsed).enabled)
|
|
357
|
+
return role;
|
|
347
358
|
const changesLaunchContext = hasRoleLaunchContextOptions(parsed);
|
|
348
359
|
const changesAgentConfig = hasAgentConfigOptions(parsed);
|
|
349
360
|
if (changesLaunchContext || changesAgentConfig) {
|
|
@@ -971,7 +982,7 @@ function presentRole(title, role, store) {
|
|
|
971
982
|
}
|
|
972
983
|
function roleListRow(role, kind) {
|
|
973
984
|
const summary = activeRoleSummary(role);
|
|
974
|
-
return [role.name, kind, summary.agent, summary.model, summary.effort, role.workspace];
|
|
985
|
+
return [role.name, kind, String(isEnabled(role)), summary.agent, summary.model, summary.effort, role.workspace];
|
|
975
986
|
}
|
|
976
987
|
function renderMissingSystemRole(name) {
|
|
977
988
|
return [
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { agentAdapterLabel as adapterLabel } from "../agent/adapterCatalog.js";
|
|
2
|
+
import { assertRoleEnabled } from "../role/availability.js";
|
|
2
3
|
import { usageError } from "../errors/cliError.js";
|
|
3
4
|
import { createRoleSessionSet } from "../executor/agentExecutor.js";
|
|
4
5
|
import { TASK_SUBMISSION_INTENTS } from "../message/message.js";
|
|
@@ -222,6 +223,7 @@ function resumeSession(args, store) {
|
|
|
222
223
|
export function applyOperatorSessionControl(control, store, now = new Date()) {
|
|
223
224
|
return store.transaction((tx) => {
|
|
224
225
|
const role = requireOperator(tx);
|
|
226
|
+
assertRoleEnabled(role);
|
|
225
227
|
const binding = role.agentBindings[control.targetAgentId];
|
|
226
228
|
if (binding === undefined) {
|
|
227
229
|
throw usageError(`Operator Agent is not bound: ${control.targetAgentId}.`);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { usageError } from "../errors/cliError.js";
|
|
2
|
+
import { isEnabled, parseEnabled } from "../role/availability.js";
|
|
2
3
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
3
4
|
import { BUILTIN_PROFILE_IDS, builtinAgentProfileInputs, createAgentProfile, updateAgentProfile } from "../profile/agentProfile.js";
|
|
4
5
|
import { resolveAgentProfileRuntime, resolveAgentProfileView } from "../profile/agentProfileRuntime.js";
|
|
@@ -35,7 +36,7 @@ export function previewProfileAgentConfigurationMutation(args, store) {
|
|
|
35
36
|
else {
|
|
36
37
|
return undefined;
|
|
37
38
|
}
|
|
38
|
-
const runtime = resolveAgentProfileRuntime(profile, store);
|
|
39
|
+
const runtime = resolveAgentProfileRuntime(profile, store, { configurationOnly: true });
|
|
39
40
|
if (runtime.status === "unavailable")
|
|
40
41
|
throw usageError(runtime.reason);
|
|
41
42
|
return {
|
|
@@ -51,6 +52,11 @@ export function runProfileCommand(args, store, now = () => new Date(), options =
|
|
|
51
52
|
case "list": return listProfiles(rest, store);
|
|
52
53
|
case "show": return showProfile(rest, store);
|
|
53
54
|
case "update": return updateProfile(rest, store, now(), options);
|
|
55
|
+
case "enable":
|
|
56
|
+
case "disable": {
|
|
57
|
+
const id = oneArg(rest, `Usage: yui config profile ${command} <id>`);
|
|
58
|
+
return updateProfile([id, "--enabled", String(command === "enable")], store, now(), options);
|
|
59
|
+
}
|
|
54
60
|
case "remove": return removeProfile(rest, store);
|
|
55
61
|
case "reset": return resetProfiles(rest, store, now());
|
|
56
62
|
default:
|
|
@@ -68,6 +74,7 @@ function addProfile(args, store, now, options) {
|
|
|
68
74
|
assertOptionPairs(parsed);
|
|
69
75
|
const profile = createAgentProfile({
|
|
70
76
|
id,
|
|
77
|
+
...(parsed.has("--enabled") ? { enabled: parseEnabled(parsed.one("--enabled")) } : {}),
|
|
71
78
|
defaultAccess: parseAccess(parsed.one("--access") ?? "read"),
|
|
72
79
|
...profileValues(parsed),
|
|
73
80
|
runtime: addedRuntime(parsed, usage)
|
|
@@ -91,6 +98,7 @@ function listProfiles(args, store) {
|
|
|
91
98
|
? "No Agent Profiles found.\n"
|
|
92
99
|
: `${renderTable("Agent Profiles", [
|
|
93
100
|
{ header: "Profile", minWidth: 7, maxWidth: 24 },
|
|
101
|
+
{ header: "Enabled", minWidth: 7, maxWidth: 8 },
|
|
94
102
|
{ header: "Revision", minWidth: 8, maxWidth: 10 },
|
|
95
103
|
{ header: "Access", minWidth: 6, maxWidth: 8 },
|
|
96
104
|
{ header: "Runtime", minWidth: 9, maxWidth: 14 },
|
|
@@ -109,6 +117,7 @@ function showProfile(args, store) {
|
|
|
109
117
|
return {
|
|
110
118
|
output: `${[
|
|
111
119
|
`Agent Profile: ${profile.id}`,
|
|
120
|
+
`Enabled: ${isEnabled(profile)}`,
|
|
112
121
|
`Revision: ${profile.revision}`,
|
|
113
122
|
`Default access: ${profile.defaultAccess}`,
|
|
114
123
|
`Description: ${profile.description ?? "-"}`,
|
|
@@ -140,8 +149,14 @@ function updateProfile(args, store, now, options) {
|
|
|
140
149
|
throw usageError("At least one Profile option is required.", usage);
|
|
141
150
|
assertOptionPairs(parsed);
|
|
142
151
|
const current = requireProfile(store, id);
|
|
152
|
+
if (parsed.seen.size === 1 && parsed.has("--enabled")
|
|
153
|
+
&& isEnabled(current) === parseEnabled(parsed.one("--enabled"))) {
|
|
154
|
+
return { output: `Agent Profile ${id} is already ${isEnabled(current) ? "enabled" : "disabled"}\n`,
|
|
155
|
+
data: resolveAgentProfileView(current, store) };
|
|
156
|
+
}
|
|
143
157
|
const runtime = updatedRuntime(current, parsed, usage);
|
|
144
158
|
const updated = updateAgentProfile(current, {
|
|
159
|
+
...(parsed.has("--enabled") ? { enabled: parseEnabled(parsed.one("--enabled")) } : {}),
|
|
145
160
|
...(parsed.has("--access") ? { defaultAccess: parseAccess(parsed.one("--access")) } : {}),
|
|
146
161
|
...profilePatch(parsed),
|
|
147
162
|
...(runtime === undefined ? {} : { runtime })
|
|
@@ -187,6 +202,7 @@ function resetProfiles(args, store, now) {
|
|
|
187
202
|
};
|
|
188
203
|
}
|
|
189
204
|
const BASE_OPTIONS = [
|
|
205
|
+
["--enabled", "value"],
|
|
190
206
|
["--access", "value"],
|
|
191
207
|
["--description", "value"],
|
|
192
208
|
["--instructions", "value"],
|
|
@@ -307,7 +323,7 @@ function assertRuntimeSourceOptions(parsed, usage) {
|
|
|
307
323
|
function validateExplicitRuntime(profile, store, options) {
|
|
308
324
|
if (profile.runtime.source !== "explicit")
|
|
309
325
|
return;
|
|
310
|
-
const runtime = resolveAgentProfileRuntime(profile, store);
|
|
326
|
+
const runtime = resolveAgentProfileRuntime(profile, store, { configurationOnly: true });
|
|
311
327
|
if (runtime.status === "unavailable")
|
|
312
328
|
throw usageError(runtime.reason);
|
|
313
329
|
options.validateAgentConfiguration?.({
|
|
@@ -320,6 +336,7 @@ function profileListRow(view) {
|
|
|
320
336
|
const { profile, runtime } = view;
|
|
321
337
|
return [
|
|
322
338
|
profile.id,
|
|
339
|
+
String(isEnabled(profile)),
|
|
323
340
|
String(profile.revision),
|
|
324
341
|
profile.defaultAccess,
|
|
325
342
|
runtime.source,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { usageError } from "../errors/cliError.js";
|
|
2
|
+
import { parseEnabled } from "../role/availability.js";
|
|
2
3
|
import { defaultRoleAgentConfig, resolveAgentAdapter } from "../executor/agentAdapter.js";
|
|
3
4
|
const PROFILE_OPTIONS = [
|
|
4
5
|
["--description", "value"],
|
|
@@ -64,6 +65,7 @@ export function roleOptionSpecs(input) {
|
|
|
64
65
|
...(input.includeAgent === true ? [["--agent", "value"]] : []),
|
|
65
66
|
...(input.includeWorkspace === true ? [["--workspace", "value"]] : []),
|
|
66
67
|
...(input.update ? [["--yes", "flag"]] : []),
|
|
68
|
+
...(input.update ? [["--enabled", "value"]] : []),
|
|
67
69
|
...PROFILE_OPTIONS,
|
|
68
70
|
...agentValueOptions,
|
|
69
71
|
...agentRepeatableOptions,
|
|
@@ -123,6 +125,7 @@ export function roleProfilePatch(parsed) {
|
|
|
123
125
|
["--skill", "--clear-skills"]
|
|
124
126
|
]);
|
|
125
127
|
return {
|
|
128
|
+
...(parsed.has("--enabled") ? { enabled: parseEnabled(parsed.one("--enabled")) } : {}),
|
|
126
129
|
...(parsed.has("--description") ? { description: requiredText(parsed.one("--description")) } : {}),
|
|
127
130
|
...(parsed.has("--clear-description") ? { description: undefined } : {}),
|
|
128
131
|
...(parsed.has("--responsibility")
|
|
@@ -3,6 +3,7 @@ import { isDeepStrictEqual } from "node:util";
|
|
|
3
3
|
import { taskMessageInboxReceipt } from "../message/taskInboxOwnership.js";
|
|
4
4
|
import { agentExecutionComponentLabel } from "../agent/executionComponents.js";
|
|
5
5
|
import { assertLeaderArchiveAdmission } from "../task/leaderArchiveAuthority.js";
|
|
6
|
+
import { assertRoleEnabled, isEnabled } from "../role/availability.js";
|
|
6
7
|
import { createRun, runExecutionObservation, runPurposeAdmitsTaskState } from "../agentRun/agentRun.js";
|
|
7
8
|
import { isGitArtifactRefString, parseGitArtifactRef } from "../artifacts/gitArtifactRef.js";
|
|
8
9
|
import { contextContentDigest, contextSnapshotRef } from "../context/contextSnapshot.js";
|
|
@@ -2251,6 +2252,11 @@ function taskRoleCommand(args, store, options) {
|
|
|
2251
2252
|
return showTaskRole(rest, store);
|
|
2252
2253
|
if (command === "update")
|
|
2253
2254
|
return updateTaskRole(rest, store, options);
|
|
2255
|
+
if (command === "enable" || command === "disable") {
|
|
2256
|
+
if (rest.length !== 2)
|
|
2257
|
+
throw usageError(`Usage: yui task role ${command} <task> <role>`);
|
|
2258
|
+
return updateTaskRole([...rest, "--enabled", String(command === "enable")], store, options);
|
|
2259
|
+
}
|
|
2254
2260
|
if (command === "remove")
|
|
2255
2261
|
return output(removeTaskRole(rest, store, options));
|
|
2256
2262
|
if (command === "bind")
|
|
@@ -2751,6 +2757,7 @@ function listTaskRoles(args, store, options) {
|
|
|
2751
2757
|
const statuses = inspectTaskRoleRuntimeStatuses(task.id, roles, store, options.runtime?.inspectTaskRolePanes?.(task.id) ?? [], options.now?.() ?? new Date());
|
|
2752
2758
|
const data = recordPage(statuses.map(status => ({
|
|
2753
2759
|
roleName: status.roleName, agentId: status.agentId, recordedHealth: status.health,
|
|
2760
|
+
enabled: isEnabled(status.role),
|
|
2754
2761
|
hostObservation: "not-requested",
|
|
2755
2762
|
openInput: taskRoleOpenInputLabel(status), activeWork: taskRoleActiveWorkLabel(status),
|
|
2756
2763
|
lastRun: taskRoleLastRunLabel(status), nativeSession: taskRoleNativeSessionLabel(status),
|
|
@@ -2810,6 +2817,9 @@ function updateTaskRole(args, store, options) {
|
|
|
2810
2817
|
taskActor(tx, options, task.id);
|
|
2811
2818
|
const role = requireRole(tx, task.id, roleName);
|
|
2812
2819
|
const changesEnvironment = parsed.has("--environment") || parsed.has("--managed-environment");
|
|
2820
|
+
if (parsed.seen.size === 1 && parsed.has("--enabled")
|
|
2821
|
+
&& isEnabled(role) === roleProfilePatch(parsed).enabled)
|
|
2822
|
+
return role;
|
|
2813
2823
|
const changesLaunchContext = hasRoleLaunchContextOptions(parsed) || parsed.has("--profile")
|
|
2814
2824
|
|| changesEnvironment;
|
|
2815
2825
|
const changesAgentConfig = hasAgentConfigOptions(parsed);
|
|
@@ -3562,6 +3572,7 @@ function dispatchWork(args, store, options) {
|
|
|
3562
3572
|
}
|
|
3563
3573
|
const roles = lanePlan.roles.map((name) => requireRole(tx, task.id, name));
|
|
3564
3574
|
for (const role of roles) {
|
|
3575
|
+
assertRoleEnabled(role);
|
|
3565
3576
|
if (tx.getActiveRun(task.id, role.name) !== null) {
|
|
3566
3577
|
throw usageError(`${task.id}/${role.name} already has an active run.`);
|
|
3567
3578
|
}
|
|
@@ -3570,6 +3581,7 @@ function dispatchWork(args, store, options) {
|
|
|
3570
3581
|
let workItemForDispatch = prepareWorkItemDispatch(item, now);
|
|
3571
3582
|
if (lanePlan.roles.length === 0) {
|
|
3572
3583
|
const role = requireRole(tx, task.id, assignee);
|
|
3584
|
+
assertRoleEnabled(role);
|
|
3573
3585
|
if (tx.getActiveRun(task.id, role.name) !== null) {
|
|
3574
3586
|
throw usageError(`${task.id}/${role.name} already has an active run.`);
|
|
3575
3587
|
}
|
|
@@ -4383,6 +4395,9 @@ function assertTaskReviewRequestLane(store, taskId, reviewerRoleName, reusableRo
|
|
|
4383
4395
|
assertReviewerAvailable(store, taskId, reviewerRoleName, reusableRound);
|
|
4384
4396
|
}
|
|
4385
4397
|
function assertReviewerAvailable(store, taskId, reviewerRoleName, reusableRound) {
|
|
4398
|
+
const role = store.getRole(taskId, reviewerRoleName);
|
|
4399
|
+
if (role !== null)
|
|
4400
|
+
assertRoleEnabled(role);
|
|
4386
4401
|
const availability = projectReviewerAvailability(store, taskId, reviewerRoleName);
|
|
4387
4402
|
if (availability.kind === "available")
|
|
4388
4403
|
return;
|
|
@@ -5001,6 +5016,7 @@ function retryRunOperation(args, store, options) {
|
|
|
5001
5016
|
throw usageError(`Task does not admit ${previous.purpose} retry: ${task.id}/${task.status}.`);
|
|
5002
5017
|
}
|
|
5003
5018
|
const role = requireRole(tx, task.id, previous.roleName);
|
|
5019
|
+
assertRoleEnabled(role);
|
|
5004
5020
|
if (tx.getActiveRun(task.id, role.name) !== null) {
|
|
5005
5021
|
throw usageError(`${task.id}/${role.name} already has an active run.`);
|
|
5006
5022
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isDeepStrictEqual } from "node:util";
|
|
2
2
|
import { runExecutionObservation } from "../agentRun/agentRun.js";
|
|
3
|
+
import { isEnabled, roleDisabledReason } from "../role/availability.js";
|
|
3
4
|
import { hasRuntimeCleanupObligation, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
|
|
4
5
|
import { currentProviderConversation, managedProviderTurnId } from "../runtime/providerRuntimeIdentity.js";
|
|
5
6
|
import { isRoleRunStalled, latestStallProgressAt } from "../scheduler/roleRunStall.js";
|
|
@@ -118,6 +119,8 @@ export function renderTaskRoleRuntimeStatus(status) {
|
|
|
118
119
|
`Task Role status: ${status.taskId}/${status.roleName}`,
|
|
119
120
|
"",
|
|
120
121
|
` Health ${status.health}`,
|
|
122
|
+
` Enabled ${isEnabled(status.role)}`,
|
|
123
|
+
...(!isEnabled(status.role) ? [` Availability ${roleDisabledReason(status.role)}`] : []),
|
|
121
124
|
` Reason ${status.healthReason}`,
|
|
122
125
|
` Open inputs ${status.openInputRequestCount}`,
|
|
123
126
|
` Agent ${status.agentId}`,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { assertRoleEnabled } from "../role/availability.js";
|
|
2
3
|
import { mkdirSync } from "node:fs";
|
|
3
4
|
import { isDeepStrictEqual } from "node:util";
|
|
4
5
|
import { join } from "node:path";
|
|
@@ -63,7 +64,8 @@ function requireTaskTarget(store, target, runnable = true) {
|
|
|
63
64
|
const task = store.getTask(target.taskId);
|
|
64
65
|
if (!task || runnable && (task.status !== "active" || task.executionGate.state !== "enabled"))
|
|
65
66
|
throw new Error("Task Agent requires an active, execution-enabled Task.");
|
|
66
|
-
|
|
67
|
+
const role = store.getRole(target.taskId, target.roleName);
|
|
68
|
+
if (!role)
|
|
67
69
|
throw new Error("Task Agent Role is unavailable.");
|
|
68
70
|
const recipient = taskRecipient(store, target);
|
|
69
71
|
if (recipient.ownerRunId) {
|
|
@@ -117,6 +119,7 @@ function ownedRole(store, home, job, runId, now) {
|
|
|
117
119
|
const jobOwner = { scope: job.scope, jobId: job.id, ...(isolated ? { runId } : {}) };
|
|
118
120
|
const existing = store.getGlobalRole(name);
|
|
119
121
|
if (existing) {
|
|
122
|
+
assertRoleEnabled(existing);
|
|
120
123
|
if (!isDeepStrictEqual(existing.jobOwner, jobOwner) || existing.activeAgentId !== job.configuration.target.agentId) {
|
|
121
124
|
throw new Error("Job Role binding changed; inspect its Session/configuration before dispatch.");
|
|
122
125
|
}
|
|
@@ -155,6 +158,9 @@ function queueAttempt(store, home, job, runId, index, now) {
|
|
|
155
158
|
const inputControl = { action: "queue", requestId: `job-agent:${runId}:${index}` };
|
|
156
159
|
let messageId;
|
|
157
160
|
if (recipient.scope === "global") {
|
|
161
|
+
const role = store.getGlobalRole(recipient.roleName);
|
|
162
|
+
if (role !== null)
|
|
163
|
+
assertRoleEnabled(role);
|
|
158
164
|
messageId = store.nextGlobalRoleMessageId();
|
|
159
165
|
let message = createGlobalRoleMessage(messageId, recipient.roleName, body, "system", { type: "system" }, now, { inputControl });
|
|
160
166
|
const set = store.getGlobalRoleSessionSet(recipient.roleName);
|
|
@@ -165,6 +171,7 @@ function queueAttempt(store, home, job, runId, index, now) {
|
|
|
165
171
|
}
|
|
166
172
|
else {
|
|
167
173
|
requireTaskTarget(store, { ...recipient });
|
|
174
|
+
assertRoleEnabled(store.getRole(recipient.taskId, recipient.roleName));
|
|
168
175
|
const resolved = taskRecipient(store, recipient);
|
|
169
176
|
messageId = store.nextMessageId(recipient.taskId);
|
|
170
177
|
store.saveMessage(recipient.taskId, createTaskMessage(messageId, recipient.taskId, body, "system", { type: "system" }, now, { ...(resolved.ownerRunId ? { recipient: resolved } : {}), inputControl }));
|
|
@@ -256,6 +263,7 @@ export function controlAgentJob(store, home, job, input, now) {
|
|
|
256
263
|
if (op === "session-new") {
|
|
257
264
|
if (!role)
|
|
258
265
|
throw new Error("Create the Job Role first.");
|
|
266
|
+
assertRoleEnabled(role);
|
|
259
267
|
assertRoleRuntimeMutationAllowed(store, { scope: "global", roleName }, "Job Session replacement");
|
|
260
268
|
const set = store.getGlobalRoleSessionSet(roleName);
|
|
261
269
|
const old = set?.sessions[set.activeAgentId];
|
|
@@ -3145,9 +3145,9 @@ function recordTaskRuntimeNativeSession(store, input, now) {
|
|
|
3145
3145
|
if (task.status === "archived") {
|
|
3146
3146
|
throw new Error(`Cannot register a native session for archived Task: ${input.taskId}.`);
|
|
3147
3147
|
}
|
|
3148
|
-
// A Draft
|
|
3149
|
-
//
|
|
3150
|
-
//
|
|
3148
|
+
// A Draft admits its first planning Run, restoration of that exact Session,
|
|
3149
|
+
// or the selected successor's initialization after verified replacement.
|
|
3150
|
+
// Ordinary Draft notifications and native hooks cannot create a Session.
|
|
3151
3151
|
const admittingRun = store.getActiveRun(input.taskId, input.roleName);
|
|
3152
3152
|
const existingPlanning = activeLiveRoleAgentSession(store.getTaskRoleSessionSet(input.taskId, input.roleName));
|
|
3153
3153
|
if (!(admittingRun !== null
|
|
@@ -3156,6 +3156,9 @@ function recordTaskRuntimeNativeSession(store, input, now) {
|
|
|
3156
3156
|
&& !(task.status === "draft" && task.executionGate.state === "enabled" && input.roleName === "leader"
|
|
3157
3157
|
&& existingPlanning?.effective.executionAuthority === "planning"
|
|
3158
3158
|
&& existingPlanning.nativeSessionId === input.nativeSessionId)
|
|
3159
|
+
&& !(task.status === "draft" && task.executionGate.state === "enabled" && input.roleName === "leader"
|
|
3160
|
+
&& input.effective?.executionAuthority === "planning"
|
|
3161
|
+
&& draftReplacementInitializationIsCurrent(store, input.taskId, input.agentId))
|
|
3159
3162
|
&& (task.status !== "active" || task.executionGate.state !== "enabled")) {
|
|
3160
3163
|
throw new Error(`Cannot register a native session for a Task that is not active: ${input.taskId}.`);
|
|
3161
3164
|
}
|
|
@@ -3191,6 +3194,32 @@ function recordTaskRuntimeNativeSession(store, input, now) {
|
|
|
3191
3194
|
store.saveRoleSessionSet(updated);
|
|
3192
3195
|
return updated.sessions[input.agentId];
|
|
3193
3196
|
}
|
|
3197
|
+
/** The existing initialization wake is the launch authority, not historical
|
|
3198
|
+
* planning success or an arbitrary "session-replaced" reason. It is retained
|
|
3199
|
+
* on startup rejection for the normal explicit wake retry/replacement path. */
|
|
3200
|
+
function draftReplacementInitializationIsCurrent(store, taskId, agentId) {
|
|
3201
|
+
const sessions = store.getTaskRoleSessionSet(taskId, "leader");
|
|
3202
|
+
if (sessions === null || sessions.activeAgentId !== agentId
|
|
3203
|
+
|| sessions.sessions[agentId] !== undefined || sessions.providerBinding !== null
|
|
3204
|
+
|| hasRuntimeCleanupObligation(store.getWorkMailbox(runtimeLifecycleTarget({
|
|
3205
|
+
scope: "task", taskId, roleName: "leader"
|
|
3206
|
+
}))))
|
|
3207
|
+
return false;
|
|
3208
|
+
const processing = store.getWorkMailbox({ kind: "role", taskId, roleName: "leader" })?.processing;
|
|
3209
|
+
if (!processing?.owner.startsWith("leader-notification:"))
|
|
3210
|
+
return false;
|
|
3211
|
+
const wake = store.getTaskWake(taskId, processing.owner.slice("leader-notification:".length));
|
|
3212
|
+
if (wake === null || !wake.reasons.includes("session-replaced"))
|
|
3213
|
+
return false;
|
|
3214
|
+
const events = store.listEventsByType(taskId, ["runtime.session-replaced", "notification.delivery"]);
|
|
3215
|
+
const replacement = events.filter(event => event.type === "runtime.session-replaced"
|
|
3216
|
+
&& event.payload.roleName === "leader").at(-1);
|
|
3217
|
+
return replacement !== undefined
|
|
3218
|
+
&& (wake.refs ?? []).some(ref => ref.type === "event" && ref.taskId === taskId && ref.id === replacement.id)
|
|
3219
|
+
&& !events.some(event => event.type === "notification.delivery"
|
|
3220
|
+
&& event.payload.attemptId === processing.batchId
|
|
3221
|
+
&& event.payload.outcome !== "deferred");
|
|
3222
|
+
}
|
|
3194
3223
|
function recordGlobalRuntimeNativeSession(store, input, now) {
|
|
3195
3224
|
const role = store.getGlobalRole(input.roleName);
|
|
3196
3225
|
if (role === null)
|
|
@@ -3271,6 +3300,7 @@ function mapRole(store, role) {
|
|
|
3271
3300
|
return {
|
|
3272
3301
|
taskId: role.taskId,
|
|
3273
3302
|
name: role.name,
|
|
3303
|
+
enabled: role.enabled !== false,
|
|
3274
3304
|
activeAgentId: role.activeAgentId,
|
|
3275
3305
|
adapterId: binding.adapterId,
|
|
3276
3306
|
...(binding.config.model === undefined ? {} : { model: binding.config.model }),
|
|
@@ -3307,8 +3337,9 @@ function taskSessionEffective(store, taskId, roleName, agentId, existing) {
|
|
|
3307
3337
|
?? store.getTaskWorkspace(taskId)
|
|
3308
3338
|
?? undefined;
|
|
3309
3339
|
const effective = resolveEffectiveLaunch({
|
|
3340
|
+
store,
|
|
3310
3341
|
role,
|
|
3311
|
-
purpose: "execution",
|
|
3342
|
+
purpose: store.getTask(taskId)?.status === "draft" && roleName === "leader" ? "planning" : "execution",
|
|
3312
3343
|
...(workspace === undefined ? {} : { workspace }),
|
|
3313
3344
|
...(item === null ? {} : { workItemWriteProjectIds: item.writeProjectIds })
|
|
3314
3345
|
});
|