@zq-silk/yui 0.2.0 → 0.4.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 +603 -133
- package/README.md +806 -31
- package/dist/agent/agent.js +2 -1
- package/dist/agent/argumentPolicy.js +3 -1
- package/dist/agent/launchEnvironment.js +106 -0
- package/dist/agent/managedRuntimeEnvironment.js +34 -0
- package/dist/brief/taskBrief.js +11 -1
- package/dist/cli/agentConfigurationPicker.js +287 -0
- package/dist/cli/commandCatalog.js +488 -60
- package/dist/cli/completion.js +146 -22
- package/dist/cli/helpRenderer.js +3 -1
- package/dist/cli/interactionCandidates.js +53 -15
- package/dist/cli/interactionPolicy.js +267 -30
- package/dist/cli/interactiveSelection.js +6 -2
- package/dist/cli/invocationRouter.js +5 -1
- package/dist/cli/operatorWizard.js +87 -0
- package/dist/cli/roleOptionCatalog.js +1 -0
- package/dist/cli/roleWizard.js +185 -21
- package/dist/cli/updateCommand.js +62 -19
- package/dist/cli/updateOrchestrator.js +539 -0
- package/dist/cli/updatePorts.js +1119 -0
- package/dist/cli/upgradeCommand.js +112 -0
- package/dist/cli.js +1420 -86
- package/dist/commands/agentCommands.js +146 -3
- package/dist/commands/configCommands.js +126 -0
- package/dist/commands/controllerCommands.js +365 -0
- package/dist/commands/globalRoleCommands.js +168 -126
- package/dist/commands/jobCommands.js +18 -8
- package/dist/commands/operatorCommands.js +159 -9
- package/dist/commands/profileCommands.js +203 -0
- package/dist/commands/projectCommands.js +650 -0
- package/dist/commands/roleConfiguration.js +85 -24
- package/dist/commands/roleRuntimeGuard.js +12 -0
- package/dist/commands/roleSkillValidation.js +47 -0
- package/dist/commands/taskActor.js +127 -0
- package/dist/commands/taskCommands.js +4201 -313
- package/dist/commands/taskCompletionGate.js +131 -0
- package/dist/commands/taskContextCommand.js +244 -30
- package/dist/commands/taskInputCommands.js +177 -59
- package/dist/commands/taskIntegrationCommands.js +303 -0
- package/dist/commands/taskOverviewCommand.js +363 -0
- package/dist/commands/taskRoleRuntimeStatus.js +125 -19
- package/dist/commands/textInput.js +15 -0
- package/dist/completion/completionInstaller.js +26 -22
- package/dist/config/yuiConfig.js +4 -3
- package/dist/context/dispatchContext.js +90 -38
- package/dist/context/roleSessionContext.js +119 -0
- package/dist/controller/claudeLifecycleHook.js +203 -0
- package/dist/controller/clientRuntime.js +408 -56
- package/dist/controller/codexLifecycleHook.js +108 -0
- package/dist/controller/controller.js +1089 -32
- package/dist/controller/domainIdentity.js +505 -0
- package/dist/controller/ephemeralResourceReaper.js +131 -0
- package/dist/controller/fileSchedulerStoreAdapter.js +2153 -103
- package/dist/controller/providerHookRunFence.js +127 -0
- package/dist/controller/resourceCleanupLinux.js +286 -0
- package/dist/controller/resourceInventory.js +531 -0
- package/dist/controller/resourceInventoryLinux.js +610 -0
- package/dist/controller/runtime.js +629 -10
- package/dist/controller/runtimeEventInbox.js +564 -0
- package/dist/controller/runtimeEventProcessor.js +248 -0
- package/dist/controller/runtimeLaunchCoordinator.js +477 -0
- package/dist/controller/sessionNotify.js +121 -78
- package/dist/coordination/deadlineScheduler.js +15 -0
- package/dist/coordination/mailboxScheduler.js +108 -0
- package/dist/coordination/workMailbox.js +329 -0
- package/dist/coordination/workMailboxQueue.js +86 -0
- package/dist/core/controllerClient.js +19 -5
- package/dist/core/controllerEndpoint.js +37 -0
- package/dist/core/controllerServer.js +218 -10
- package/dist/core/protocol.js +6 -2
- package/dist/decision/decision.js +2 -1
- package/dist/doctor/doctor.js +681 -32
- package/dist/domain/validation.js +53 -0
- package/dist/errors/cliError.js +5 -3
- package/dist/event/taskEvent.js +7 -3
- package/dist/execution/codexThreadNaming.js +160 -0
- package/dist/execution/executionGroup.js +579 -0
- package/dist/executor/agentAdapter.js +255 -40
- package/dist/executor/agentConfigurationCatalog.js +326 -0
- package/dist/executor/agentConfigurationProbe.js +506 -0
- package/dist/executor/agentExecutor.js +625 -10
- package/dist/executor/codexConfigConflict.js +290 -0
- package/dist/executor/effectiveLaunch.js +340 -0
- package/dist/executor/executorRegistry.js +238 -36
- package/dist/executor/fileRoleLaunchPlanner.js +550 -40
- package/dist/executor/turnCompletion.js +126 -0
- package/dist/input/inputRequest.js +30 -9
- package/dist/integration/changeSet.js +36 -0
- package/dist/integration/checkResult.js +24 -0
- package/dist/integration/gitIntegrationService.js +695 -0
- package/dist/integration/integrationAttempt.js +142 -0
- package/dist/interaction/operatorPresentation.js +96 -0
- package/dist/lifecycle/canonicalLifecycleEvent.js +342 -0
- package/dist/lifecycle/exactRunTerminalization.js +572 -0
- package/dist/lifecycle/providerLifecycleMapping.js +190 -0
- package/dist/lifecycle/taskRoleSessionReset.js +124 -0
- package/dist/message/message.js +23 -7
- package/dist/milestone/milestone.js +2 -1
- package/dist/operator/operatorSessionHistory.js +124 -0
- package/dist/output/agentConfigurationPresentation.js +43 -0
- package/dist/output/rolePresentation.js +34 -10
- package/dist/output/terminal.js +8 -0
- package/dist/output/timePresentation.js +55 -0
- package/dist/profile/agentProfile.js +128 -0
- package/dist/repository/gitWorkspace.js +578 -24
- package/dist/repository/project.js +213 -0
- package/dist/repository/taskWorkspaceCoordinator.js +392 -0
- package/dist/repository/taskWorkspacePreparer.js +1688 -191
- package/dist/review/reviewConfig.js +11 -0
- package/dist/review/reviewRound.js +399 -0
- package/dist/review/taskFinalReviewContract.js +90 -0
- package/dist/role/role.js +124 -23
- package/dist/run/agentRun.js +155 -12
- package/dist/run/runIdentity.js +82 -0
- package/dist/runtime/exactControlPlane.js +472 -0
- package/dist/runtime/index.js +8 -0
- package/dist/runtime/lifecycleReservation.js +38 -0
- package/dist/runtime/ports.js +11 -0
- package/dist/runtime/preallocatedNativeSession.js +13 -0
- package/dist/runtime/promptEnvelope.js +30 -0
- package/dist/runtime/runtimeBinding.js +31 -0
- package/dist/runtime/runtimeOwner.js +14 -0
- package/dist/runtime/sessionLaunchRequest.js +62 -0
- package/dist/runtime/sessionTitle.js +54 -0
- package/dist/runtime/taskRuntimeIsolation.js +643 -0
- package/dist/runtime/tmuxAdapters.js +315 -0
- package/dist/runtime/turnCompletion.js +3 -0
- package/dist/runtime/validation.js +23 -0
- package/dist/scheduler/activeRoleRunDelivery.js +342 -32
- package/dist/scheduler/activeTaskProgress.js +63 -0
- package/dist/scheduler/leaderFailure.js +2 -1
- package/dist/scheduler/leaderWakeupProcessor.js +307 -66
- package/dist/scheduler/operatorInputNotificationProcessor.js +109 -46
- package/dist/scheduler/operatorNotification.js +44 -2
- package/dist/scheduler/ports.js +28 -1
- package/dist/scheduler/roleRunLiveness.js +131 -25
- package/dist/scheduler/roleRunStall.js +951 -0
- package/dist/scheduler/taskExecutionProjection.js +544 -0
- package/dist/scheduler/wakeupQueue.js +3 -0
- package/dist/setup/setupCommand.js +302 -52
- package/dist/storage/compatibleTaskStore.js +102 -0
- package/dist/storage/migration/baseline.js +78 -0
- package/dist/storage/migration/classifier.js +51 -0
- package/dist/storage/migration/compatibleCodec.js +53 -0
- package/dist/storage/migration/engine.js +147 -0
- package/dist/storage/migration/index.js +33 -0
- package/dist/storage/migration/planner.js +154 -0
- package/dist/storage/migration/productionRegistry.js +486 -0
- package/dist/storage/migration/registry.js +169 -0
- package/dist/storage/migration/report.js +54 -0
- package/dist/storage/migration/types.js +31 -0
- package/dist/storage/storageSchema.js +147 -123
- package/dist/storage/storageVersions.js +11 -0
- package/dist/storage/taskStore.js +1793 -197
- package/dist/storage/upgrade/homeClassification.js +156 -0
- package/dist/storage/upgrade/homeMigrationTarget.js +595 -0
- package/dist/storage/upgrade/offlineUpgradeInventory.js +315 -0
- package/dist/storage/upgrade/productionMigrationRegistry.js +6 -0
- package/dist/storage/upgrade/recordVersionScan.js +176 -0
- package/dist/storage/upgrade/recordVersions.js +159 -0
- package/dist/storage/upgrade/switchProgress.js +80 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +948 -0
- package/dist/storage/upgrade/upgradeReceipt.js +161 -0
- package/dist/storage/upgradeCoordination.js +186 -0
- package/dist/storage/upgradeFence.js +366 -0
- package/dist/task/task.js +132 -26
- package/dist/task/taskRecordReference.js +66 -0
- package/dist/tmux/commandExecutor.js +75 -2
- package/dist/tmux/tmuxManager.js +747 -49
- package/dist/version.js +23 -0
- package/dist/web/assets/assetManifest.js +62 -0
- package/dist/web/assets/client/app.js +631 -0
- package/dist/web/assets/client/components.js +605 -0
- package/dist/web/assets/client/dom.js +14 -0
- package/dist/web/assets/client/format.js +28 -0
- package/dist/web/assets/client/i18n.js +494 -0
- package/dist/web/assets/client/markdown.js +114 -0
- package/dist/web/assets/client/theme.js +32 -0
- package/dist/web/assets/client/view.js +458 -0
- package/dist/web/assets/fontData.js +12 -0
- package/dist/web/assets/fonts.js +12 -0
- package/dist/web/assets/shell.js +114 -0
- package/dist/web/assets/styles/cards.js +135 -0
- package/dist/web/assets/styles/layout.js +47 -0
- package/dist/web/assets/styles/markdown.js +29 -0
- package/dist/web/assets/styles/responsive.js +39 -0
- package/dist/web/assets/styles/tokens.js +101 -0
- package/dist/web/assets/styles/widgets.js +147 -0
- package/dist/web/tmuxWebTerminal.js +158 -0
- package/dist/web/webServer.js +463 -0
- package/dist/web/webSnapshot.js +148 -0
- package/dist/workItem/workItem.js +642 -23
- package/dist/workspace/gitChangeSetCapture.js +86 -0
- package/dist/workspace/workItemChangeSetManager.js +445 -0
- package/dist/worktree/managedWorkspace.js +202 -0
- package/docs/task-local-identity.md +62 -0
- package/i18n/README.zh-CN.md +406 -31
- package/package.json +10 -2
- package/skills/yui-leader/SKILL.md +601 -39
- package/skills/yui-operator/SKILL.md +255 -34
- package/skills/yui-reviewer/SKILL.md +57 -0
- package/skills/yui-worker/SKILL.md +214 -17
- package/dist/commands/repositoryCommands.js +0 -86
- package/dist/operator/operatorContext.js +0 -66
- package/dist/repository/repository.js +0 -55
- package/dist/scheduler/archivedTaskRuntime.js +0 -12
- package/dist/worktree/roleWorkspace.js +0 -62
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { hasRuntimeCleanupObligation, hasRuntimeLifecycleWork, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
|
|
3
|
+
import { isRoleRunStalled, latestStallProgressAt } from "../scheduler/roleRunStall.js";
|
|
1
4
|
export function inspectTaskRoleRuntimeStatuses(taskId, roles, store, panes) {
|
|
2
5
|
const taskOpenInputRequestCount = store.listInputRequests(taskId)
|
|
3
6
|
.filter((request) => request.status === "open").length;
|
|
@@ -12,13 +15,16 @@ export function inspectTaskRoleRuntimeStatuses(taskId, roles, store, panes) {
|
|
|
12
15
|
export function renderTaskRoleRuntimeStatus(status) {
|
|
13
16
|
const activeRun = status.activeRun === null
|
|
14
17
|
? "-"
|
|
15
|
-
: `${status.activeRun.id} (${status.activeRun
|
|
18
|
+
: `${status.activeRun.id} (${activeRunDeliveryLabel(status.activeRun)})`;
|
|
16
19
|
const activeWork = status.activeWork === null
|
|
17
20
|
? "-"
|
|
18
21
|
: `${status.activeWork.id} (${status.activeWork.status}) ${status.activeWork.title}`;
|
|
19
22
|
const nativeSession = status.nativeSession === null
|
|
20
23
|
? "not recorded"
|
|
21
|
-
: `${status.nativeSession.nativeSessionId} (${status.nativeSession.status}, ${status.nativeSession.adapterId})`;
|
|
24
|
+
: `${status.nativeSession.nativeSessionId} (${status.nativeSession.status}, ${status.nativeSession.adapterId}, effective r${status.nativeSession.effective.sourceDesiredRevision})`;
|
|
25
|
+
const effectiveLaunch = status.effectiveLaunch === null
|
|
26
|
+
? "not started"
|
|
27
|
+
: `${status.effectiveLaunch.agentId}/${status.effectiveLaunch.adapterId}; r${status.effectiveLaunch.sourceDesiredRevision}; Profile intent=${status.effectiveLaunch.profileAccess}; permission=${status.effectiveLaunch.permission.strategy}`;
|
|
22
28
|
const tmux = status.tmux.state === "missing"
|
|
23
29
|
? "missing"
|
|
24
30
|
: [
|
|
@@ -28,11 +34,7 @@ export function renderTaskRoleRuntimeStatus(status) {
|
|
|
28
34
|
status.tmux.target
|
|
29
35
|
].filter((value) => value !== undefined).join(", ");
|
|
30
36
|
const workspaceDetails = status.workspace.managed
|
|
31
|
-
?
|
|
32
|
-
` Repository ${status.workspace.repositoryId}`,
|
|
33
|
-
` Branch ${status.workspace.branch}`,
|
|
34
|
-
` Base ${status.workspace.baseRef} (${status.workspace.baseCommit})`
|
|
35
|
-
]
|
|
37
|
+
? status.workspace.entries.map((entry) => (` Project ${entry.directory} (${entry.access}) ${entry.branch} @ ${entry.baseCommit}`))
|
|
36
38
|
: [];
|
|
37
39
|
return [
|
|
38
40
|
`Task Role status: ${status.taskId}/${status.roleName}`,
|
|
@@ -41,12 +43,23 @@ export function renderTaskRoleRuntimeStatus(status) {
|
|
|
41
43
|
` Reason ${status.healthReason}`,
|
|
42
44
|
` Open inputs ${status.openInputRequestCount}`,
|
|
43
45
|
` Agent ${status.agentId}`,
|
|
46
|
+
` Desired launch r${status.desiredRevision}; Profile intent=${status.role.defaultAccess}`,
|
|
47
|
+
` Effective launch ${effectiveLaunch}`,
|
|
48
|
+
` Desired drift ${status.effectiveLaunch === null
|
|
49
|
+
? "-"
|
|
50
|
+
: status.launchDrift ? "pending next launch" : "none"}`,
|
|
51
|
+
` Run/session ${status.runSessionDrift ? "snapshot mismatch" : "snapshot consistent"}`,
|
|
44
52
|
` Role state ${status.role.status}`,
|
|
45
53
|
` Active work ${activeWork}`,
|
|
46
54
|
` Active run ${activeRun}`,
|
|
55
|
+
` Run attention ${status.stall.active
|
|
56
|
+
? `needs-attention (${status.stall.kind ?? "execution-stalled"}; no durable progress since ${status.stall.progressAt ?? "unknown"})`
|
|
57
|
+
: "none"}`,
|
|
47
58
|
` Native session ${nativeSession}`,
|
|
59
|
+
` Runtime cleanup ${status.runtimeCleanupPending ? "pending" : "none"}`,
|
|
60
|
+
` Fresh launch ${status.freshLaunchAllowed ? "allowed" : "blocked"}`,
|
|
48
61
|
` tmux pane ${tmux}`,
|
|
49
|
-
` Workspace ${status.workspace.path}`,
|
|
62
|
+
` Workspace ${status.workspace.managed ? status.workspace.root : status.workspace.path}`,
|
|
50
63
|
...workspaceDetails
|
|
51
64
|
].join("\n").concat("\n");
|
|
52
65
|
}
|
|
@@ -56,8 +69,22 @@ export function taskRoleActiveWorkLabel(status) {
|
|
|
56
69
|
return status.activeRun === null ? "-" : status.activeRun.id;
|
|
57
70
|
}
|
|
58
71
|
export function taskRoleNativeSessionLabel(status) {
|
|
72
|
+
if (status.runtimeCleanupPending && status.nativeSession === null)
|
|
73
|
+
return "reset (cleanup-pending)";
|
|
59
74
|
return status.nativeSession?.status ?? "unbound";
|
|
60
75
|
}
|
|
76
|
+
export function inspectTaskRoleSessionRecovery(taskId, roleName, store) {
|
|
77
|
+
const sessions = store.getTaskRoleSessionSet(taskId, roleName);
|
|
78
|
+
const target = runtimeLifecycleTarget({ scope: "task", taskId, roleName });
|
|
79
|
+
const runtimeMailbox = store.getWorkMailbox(target);
|
|
80
|
+
return {
|
|
81
|
+
taskId,
|
|
82
|
+
roleName,
|
|
83
|
+
runtimeCleanupPending: hasRuntimeCleanupObligation(runtimeMailbox),
|
|
84
|
+
freshLaunchAllowed: (sessions === null
|
|
85
|
+
|| sessions.sessions[sessions.activeAgentId] === undefined) && !hasRuntimeLifecycleWork(runtimeMailbox)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
61
88
|
export function taskRoleOpenInputLabel(status) {
|
|
62
89
|
return status.roleName === "leader" && status.openInputRequestCount > 0
|
|
63
90
|
? String(status.openInputRequestCount)
|
|
@@ -76,7 +103,15 @@ function inspectTaskRoleRuntimeStatus(taskId, role, store, pane, openInputReques
|
|
|
76
103
|
? null
|
|
77
104
|
: store.getWorkItem(taskId, activeRun.workItemId);
|
|
78
105
|
const sessions = store.getTaskRoleSessionSet(taskId, role.name);
|
|
79
|
-
const
|
|
106
|
+
const effectiveAgentId = activeRun?.effective.agentId ?? sessions?.activeAgentId;
|
|
107
|
+
const nativeSession = effectiveAgentId === undefined
|
|
108
|
+
? null
|
|
109
|
+
: sessions?.sessions[effectiveAgentId] ?? null;
|
|
110
|
+
const effectiveLaunch = activeRun?.effective ?? nativeSession?.effective ?? null;
|
|
111
|
+
const runSessionDrift = activeRun !== null
|
|
112
|
+
&& nativeSession !== null
|
|
113
|
+
&& !isDeepStrictEqual(activeRun.effective, nativeSession.effective);
|
|
114
|
+
const recovery = inspectTaskRoleSessionRecovery(taskId, role.name, store);
|
|
80
115
|
const tmux = pane === undefined
|
|
81
116
|
? { state: "missing" }
|
|
82
117
|
: {
|
|
@@ -86,15 +121,42 @@ function inspectTaskRoleRuntimeStatus(taskId, role, store, pane, openInputReques
|
|
|
86
121
|
...(pane.pid === undefined ? {} : { pid: pane.pid }),
|
|
87
122
|
currentCommand: pane.currentCommand
|
|
88
123
|
};
|
|
89
|
-
|
|
124
|
+
// The active Run snapshot is authoritative for the live Role session. It
|
|
125
|
+
// may point at a ReviewRound-owned workspace, which is intentionally
|
|
126
|
+
// distinct from the WorkItem Develop workspace.
|
|
127
|
+
const managedWorkspace = activeRun?.workspace
|
|
128
|
+
?? (activeRun?.workItemId === undefined
|
|
129
|
+
? store.getTaskWorkspace(taskId)
|
|
130
|
+
: store.getWorkItemWorkspace(taskId, activeRun.workItemId));
|
|
90
131
|
const workspace = managedWorkspace === null
|
|
91
132
|
? { managed: false, path: role.workspace }
|
|
92
133
|
: { ...managedWorkspace, managed: true };
|
|
93
|
-
const
|
|
134
|
+
const events = store.listEvents(taskId);
|
|
135
|
+
const stalled = activeRun !== null && isRoleRunStalled(events, activeRun.id);
|
|
136
|
+
const stallProgressAt = activeRun === null
|
|
137
|
+
? undefined
|
|
138
|
+
: latestStallProgressAt(events, activeRun.id);
|
|
139
|
+
const stallKind = activeRun === null
|
|
140
|
+
? undefined
|
|
141
|
+
: latestStallKind(events, activeRun.id);
|
|
142
|
+
const health = calculateHealth(role, activeRun, nativeSession, recovery.runtimeCleanupPending, tmux, openInputRequestCount, stalled);
|
|
143
|
+
const stall = activeRun === null
|
|
144
|
+
? { active: false }
|
|
145
|
+
: {
|
|
146
|
+
active: stalled,
|
|
147
|
+
...(stallProgressAt === undefined
|
|
148
|
+
? {}
|
|
149
|
+
: { progressAt: stallProgressAt }),
|
|
150
|
+
...(stallKind === undefined ? {} : { kind: stallKind })
|
|
151
|
+
};
|
|
94
152
|
return {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
153
|
+
...recovery,
|
|
154
|
+
agentId: effectiveLaunch?.agentId ?? role.activeAgentId,
|
|
155
|
+
desiredRevision: role.launchRevision,
|
|
156
|
+
effectiveLaunch,
|
|
157
|
+
launchDrift: effectiveLaunch !== null
|
|
158
|
+
&& effectiveLaunch.sourceDesiredRevision !== role.launchRevision,
|
|
159
|
+
runSessionDrift,
|
|
98
160
|
...health,
|
|
99
161
|
openInputRequestCount,
|
|
100
162
|
role,
|
|
@@ -102,16 +164,40 @@ function inspectTaskRoleRuntimeStatus(taskId, role, store, pane, openInputReques
|
|
|
102
164
|
activeWork,
|
|
103
165
|
nativeSession,
|
|
104
166
|
tmux,
|
|
105
|
-
workspace
|
|
167
|
+
workspace,
|
|
168
|
+
stall
|
|
106
169
|
};
|
|
107
170
|
}
|
|
108
|
-
function
|
|
171
|
+
function latestStallKind(events, runId) {
|
|
172
|
+
const event = [...events]
|
|
173
|
+
.filter((candidate) => candidate.type === "run.stalled" && candidate.payload.runId === runId)
|
|
174
|
+
.sort((left, right) => Date.parse(right.createdAt) - Date.parse(left.createdAt))[0];
|
|
175
|
+
return event?.payload.kind === "delivery-stalled" || event?.payload.kind === "execution-stalled"
|
|
176
|
+
? event.payload.kind
|
|
177
|
+
: undefined;
|
|
178
|
+
}
|
|
179
|
+
function calculateHealth(role, activeRun, nativeSession, runtimeCleanupPending, tmux, openInputRequestCount, stalled) {
|
|
180
|
+
if (runtimeCleanupPending && nativeSession === null) {
|
|
181
|
+
return {
|
|
182
|
+
health: "needs-attention",
|
|
183
|
+
healthReason: "the native Session was reset; verified runtime cleanup is pending"
|
|
184
|
+
};
|
|
185
|
+
}
|
|
109
186
|
if (role.status === "failed" || role.status === "exited") {
|
|
110
187
|
return { health: "failed", healthReason: `persisted Role state is ${role.status}` };
|
|
111
188
|
}
|
|
112
189
|
if (nativeSession?.status === "broken") {
|
|
113
190
|
return { health: "failed", healthReason: "the active native session is broken" };
|
|
114
191
|
}
|
|
192
|
+
const awaitingProviderAcceptance = activeRun?.pushedAt !== undefined
|
|
193
|
+
&& activeRun.deliveredAt === undefined;
|
|
194
|
+
if (awaitingProviderAcceptance
|
|
195
|
+
&& tmux.state !== "running") {
|
|
196
|
+
return {
|
|
197
|
+
health: "needs-attention",
|
|
198
|
+
healthReason: "the pushed active Run is awaiting provider acceptance and has no live tmux pane"
|
|
199
|
+
};
|
|
200
|
+
}
|
|
115
201
|
if (tmux.state === "exited") {
|
|
116
202
|
return { health: "failed", healthReason: "the tmux pane has exited" };
|
|
117
203
|
}
|
|
@@ -125,6 +211,12 @@ function calculateHealth(role, activeRun, nativeSession, tmux, openInputRequestC
|
|
|
125
211
|
if (activeRun.deliveredAt !== undefined && tmux.state !== "running") {
|
|
126
212
|
return { health: "needs-attention", healthReason: "the delivered active Run has no live tmux pane" };
|
|
127
213
|
}
|
|
214
|
+
if (stalled) {
|
|
215
|
+
return {
|
|
216
|
+
health: "needs-attention",
|
|
217
|
+
healthReason: "the live active Run has no durable progress in the configured stall window"
|
|
218
|
+
};
|
|
219
|
+
}
|
|
128
220
|
}
|
|
129
221
|
if (activeRun === null && role.status === "running") {
|
|
130
222
|
return { health: "needs-attention", healthReason: "the Role is running without an active Run" };
|
|
@@ -142,11 +234,25 @@ function calculateHealth(role, activeRun, nativeSession, tmux, openInputRequestC
|
|
|
142
234
|
};
|
|
143
235
|
}
|
|
144
236
|
if (activeRun !== null) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
237
|
+
if (activeRun.deliveredAt !== undefined) {
|
|
238
|
+
return { health: "running", healthReason: "the active Run has a live tmux pane" };
|
|
239
|
+
}
|
|
240
|
+
if (activeRun.pushedAt !== undefined) {
|
|
241
|
+
return {
|
|
242
|
+
health: "awaiting-provider-acceptance",
|
|
243
|
+
healthReason: "the pushed active Run is awaiting provider acceptance"
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
return { health: "starting", healthReason: "the active Run is awaiting tmux delivery" };
|
|
148
247
|
}
|
|
149
248
|
return tmux.state === "running"
|
|
150
249
|
? { health: "ready", healthReason: "the native Agent pane is ready without active work" }
|
|
151
250
|
: { health: "idle", healthReason: "there is no active work or live tmux pane" };
|
|
152
251
|
}
|
|
252
|
+
function activeRunDeliveryLabel(run) {
|
|
253
|
+
if (run.deliveredAt !== undefined)
|
|
254
|
+
return "delivered";
|
|
255
|
+
if (run.pushedAt !== undefined)
|
|
256
|
+
return "pushed (awaiting provider acceptance)";
|
|
257
|
+
return "queued";
|
|
258
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { usageError } from "../errors/cliError.js";
|
|
3
|
+
export function readCommandText(inline, file, label, usage) {
|
|
4
|
+
if ((inline === undefined) === (file === undefined)) {
|
|
5
|
+
throw usageError(`Specify exactly one of ${label} or ${label}-file.`, usage);
|
|
6
|
+
}
|
|
7
|
+
const value = file === undefined
|
|
8
|
+
? inline
|
|
9
|
+
: readFileSync(file === "-" ? 0 : file, "utf8");
|
|
10
|
+
const normalized = value.trim();
|
|
11
|
+
if (normalized.length === 0 || normalized.includes("\0")) {
|
|
12
|
+
throw usageError(`${label} is required.`, usage);
|
|
13
|
+
}
|
|
14
|
+
return normalized;
|
|
15
|
+
}
|
|
@@ -6,34 +6,38 @@ import { activationBlock, activationEnd, activationIsAutomatic, activationStart,
|
|
|
6
6
|
export function installCompletion(store, shell, installation, env, identity, activate) {
|
|
7
7
|
validateInstallation(installation);
|
|
8
8
|
writeManagedScript(shell, installation.scriptPath, identity);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
store.transaction((tx) => {
|
|
10
|
+
const config = tx.getConfig();
|
|
11
|
+
tx.saveConfig({
|
|
12
|
+
...config,
|
|
13
|
+
completionInstallations: {
|
|
14
|
+
...config.completionInstallations,
|
|
15
|
+
[shell]: installation
|
|
16
|
+
}
|
|
17
|
+
});
|
|
16
18
|
});
|
|
17
19
|
if (!activationIsAutomatic(shell, installation, env, identity) && activate) {
|
|
18
20
|
writeActivationBlock(shell, installation, identity);
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
23
|
export function uninstallCompletion(store, shell, identity) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
store.transaction((tx) => {
|
|
25
|
+
const config = tx.getConfig();
|
|
26
|
+
const installation = config.completionInstallations?.[shell];
|
|
27
|
+
if (installation === undefined)
|
|
28
|
+
return;
|
|
29
|
+
assertManagedScriptRemovable(shell, installation.scriptPath, identity);
|
|
30
|
+
assertActivationRemovable(shell, installation, identity);
|
|
31
|
+
removeManagedScript(shell, installation.scriptPath, identity);
|
|
32
|
+
removeActivationBlock(shell, installation, identity);
|
|
33
|
+
const installations = { ...config.completionInstallations };
|
|
34
|
+
delete installations[shell];
|
|
35
|
+
if (Object.keys(installations).length > 0) {
|
|
36
|
+
tx.saveConfig({ ...config, completionInstallations: installations });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const { completionInstallations: _removed, ...withoutInstallations } = config;
|
|
40
|
+
tx.saveConfig(withoutInstallations);
|
|
37
41
|
});
|
|
38
42
|
}
|
|
39
43
|
function assertManagedScriptRemovable(shell, path, identity) {
|
package/dist/config/yuiConfig.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export const DEFAULT_RECONCILIATION_INTERVAL_SECONDS =
|
|
1
|
+
export const DEFAULT_RECONCILIATION_INTERVAL_SECONDS = 120;
|
|
2
2
|
export const MIN_RECONCILIATION_INTERVAL_SECONDS = 5;
|
|
3
3
|
export const MAX_RECONCILIATION_INTERVAL_SECONDS = 300;
|
|
4
4
|
/**
|
|
5
|
-
* Resolves the durable Yui setting used for
|
|
6
|
-
*
|
|
5
|
+
* Resolves the durable Yui setting used for low-frequency recovery
|
|
6
|
+
* reconciliation. Normal durable state changes wake the Controller through
|
|
7
|
+
* its event queue and do not wait for this interval.
|
|
7
8
|
*/
|
|
8
9
|
export function reconciliationIntervalMilliseconds(value) {
|
|
9
10
|
const seconds = value ?? DEFAULT_RECONCILIATION_INTERVAL_SECONDS;
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { dataError } from "../errors/cliError.js";
|
|
4
1
|
/** Compatibility entry point used by the restored Task workflow. */
|
|
5
|
-
export function compileDispatchInput(_store, taskId, role, input) {
|
|
6
|
-
|
|
7
|
-
return buildRoleContext({ taskId, role, input, configuredSkillBodies });
|
|
2
|
+
export function compileDispatchInput(_store, taskId, role, input, workContext = {}) {
|
|
3
|
+
return buildRoleContext({ taskId, role, input, ...workContext });
|
|
8
4
|
}
|
|
9
5
|
export function buildRoleContext(context) {
|
|
10
6
|
return context.role.name === "leader"
|
|
@@ -17,53 +13,109 @@ export function buildLeaderContext(context) {
|
|
|
17
13
|
export function buildWorkerContext(context) {
|
|
18
14
|
return renderDispatchContext("worker", context);
|
|
19
15
|
}
|
|
16
|
+
const WORKER_RUN_COMPLETION_MARKER = "Yui Role Run completion requirement:";
|
|
17
|
+
const WORKER_RUN_COMPLETION_REQUIREMENT = [
|
|
18
|
+
WORKER_RUN_COMPLETION_MARKER,
|
|
19
|
+
"Before ending, read the exact current Run ID from the managed first line and execute "
|
|
20
|
+
+ "`yui task run yield <current-run-id> --summary-file - <<'YUI_SUMMARY'` "
|
|
21
|
+
+ "followed by the outcome, evidence, and a closing `YUI_SUMMARY` line, "
|
|
22
|
+
+ "replacing the placeholder with that ID.",
|
|
23
|
+
"If you cannot finally determine success, failure, completeness, or the correct "
|
|
24
|
+
+ "disposition, do not guess, silently stop, or hide uncertainty behind a success "
|
|
25
|
+
+ "summary. Use this exact yield path and label the handoff uncertain, incomplete, "
|
|
26
|
+
+ "blocked, or requiring Leader judgment.",
|
|
27
|
+
"Report the most complete truthful evidence available and, when applicable: exact Run, "
|
|
28
|
+
+ "WorkItem, and native Session identity; actions actually performed; changed paths "
|
|
29
|
+
+ "and commit/worktree state; checks actually run and their outcomes; provider, runtime, "
|
|
30
|
+
+ "or permission errors; the last confirmed lifecycle boundary; work not performed; "
|
|
31
|
+
+ "unresolved assumptions or decisions; residual risks; confidence; and bounded next options.",
|
|
32
|
+
"Yield submits immutable Run evidence and a Candidate, or Review evidence only. It never "
|
|
33
|
+
+ "implies Leader acceptance, WorkItem completion, ChangeSet capture, Integration, or "
|
|
34
|
+
+ "Task completion. Review Runs report findings, verification gaps, and limits; the "
|
|
35
|
+
+ "Leader decides disposition.",
|
|
36
|
+
"This exact control-plane permission does not grant repository writes, broad Bash, "
|
|
37
|
+
+ "external effects, or cross-Run control.",
|
|
38
|
+
"If the exact yield is denied, do not retry, broaden permissions, use a wrapper, mutate "
|
|
39
|
+
+ "Yui state, or invent delivery evidence. Truthfully surface the blocker through the "
|
|
40
|
+
+ "supported provider failure boundary; there is no fallback protocol.",
|
|
41
|
+
"Yielding closes the Run and hands the WorkItem to the Leader for acceptance; "
|
|
42
|
+
+ "a final response alone does neither.",
|
|
43
|
+
"The yield command must be your final tool action. After it succeeds, stop immediately: "
|
|
44
|
+
+ "do not inspect, poll, accept, or perform any further work in the same native turn."
|
|
45
|
+
].join("\n");
|
|
46
|
+
export function ensureWorkerRunCompletionRequirement(input) {
|
|
47
|
+
return input.endsWith(`\n\n${WORKER_RUN_COMPLETION_REQUIREMENT}`)
|
|
48
|
+
? input
|
|
49
|
+
: `${input}\n\n${WORKER_RUN_COMPLETION_REQUIREMENT}`;
|
|
50
|
+
}
|
|
20
51
|
function renderDispatchContext(kind, context) {
|
|
21
52
|
const profile = context.role;
|
|
53
|
+
const binding = profile.agentBindings[profile.activeAgentId];
|
|
22
54
|
const profileLines = [
|
|
23
55
|
`Task: ${context.taskId}`,
|
|
24
56
|
`Role: ${profile.name}`,
|
|
25
57
|
`Active Agent: ${profile.activeAgentId}`,
|
|
58
|
+
`Runtime: ${binding.adapterId}; model: ${binding.config.model ?? "CLI default"}; effort: ${binding.config.effort ?? "CLI default"}; permission: ${binding.config.permission.strategy}`,
|
|
26
59
|
profile.description === undefined ? null : `Description: ${profile.description}`,
|
|
27
60
|
...(profile.responsibilities ?? []).map((item) => `Responsibility: ${item}`),
|
|
28
61
|
...(profile.constraints ?? []).map((item) => `Constraint: ${item}`),
|
|
29
|
-
profile.expectedOutput === undefined ? null : `Expected output: ${profile.expectedOutput}
|
|
30
|
-
profile.systemPrompt === undefined ? null : `Role instruction: ${profile.systemPrompt}`,
|
|
31
|
-
profile.skills === undefined || profile.skills.length === 0
|
|
32
|
-
? null
|
|
33
|
-
: `Configured role skills: ${profile.skills.join(", ")}`
|
|
62
|
+
profile.expectedOutput === undefined ? null : `Expected output: ${profile.expectedOutput}`
|
|
34
63
|
].filter((line) => line !== null);
|
|
35
|
-
|
|
36
|
-
|
|
64
|
+
const workLines = kind === "worker"
|
|
65
|
+
? renderWorkerScope(context)
|
|
66
|
+
: [];
|
|
67
|
+
const rendered = [
|
|
68
|
+
`Follow the injected yui-${kind} Skill for this Yui dispatch.`,
|
|
69
|
+
renderContextLayers(context, kind),
|
|
37
70
|
profileLines.join("\n"),
|
|
38
|
-
|
|
71
|
+
workLines.length === 0 ? null : workLines.join("\n"),
|
|
39
72
|
"Yui dispatch:",
|
|
40
73
|
requireText(context.input, "dispatch input")
|
|
41
|
-
].filter(
|
|
74
|
+
].filter((section) => section !== null && section.length > 0)
|
|
75
|
+
.join("\n\n");
|
|
76
|
+
return kind === "worker"
|
|
77
|
+
? ensureWorkerRunCompletionRequirement(rendered)
|
|
78
|
+
: rendered;
|
|
42
79
|
}
|
|
43
|
-
function
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
throw error;
|
|
62
|
-
}
|
|
63
|
-
});
|
|
80
|
+
function renderContextLayers(context, kind) {
|
|
81
|
+
const projects = context.projectPolicy ?? [];
|
|
82
|
+
const policyLines = projects.length === 0
|
|
83
|
+
? ["- none (this Task is not Project-backed)"]
|
|
84
|
+
: projects.map(({ projectId, directory }) => {
|
|
85
|
+
const label = directory === undefined ? projectId : `${directory} (${projectId})`;
|
|
86
|
+
return `- ${label}: \`yui project show ${projectId}\`; then \`yui project knowledge list ${projectId}\` and show the relevant entries`;
|
|
87
|
+
});
|
|
88
|
+
return [
|
|
89
|
+
"Context layers:",
|
|
90
|
+
"- Yui Core: durable identity, lifecycle, access, workspace, and exact handoff safety.",
|
|
91
|
+
`- Generic ${kind} Skill: reusable role behavior and evidence discipline.`,
|
|
92
|
+
"- Project Policy: project-owned build, test, migration, release, and review rules; it is not a Yui Core default.",
|
|
93
|
+
...["Project Policy references:", ...policyLines],
|
|
94
|
+
"- Task Contract: the current Task brief, WorkItem objective, acceptance criteria, and dispatch input.",
|
|
95
|
+
"Do not import rules from another Project or Task, and do not infer Project Policy from repository files alone."
|
|
96
|
+
].join("\n");
|
|
64
97
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
98
|
+
function renderWorkerScope(context) {
|
|
99
|
+
if (context.workItem === undefined || context.workspace === undefined)
|
|
100
|
+
return [];
|
|
101
|
+
const writable = context.workspace.entries.filter(({ access }) => access === "write");
|
|
102
|
+
const contextOnly = context.workspace.entries.filter(({ access }) => access === "read");
|
|
103
|
+
const projectLines = (label, entries) => [
|
|
104
|
+
`${label}:`,
|
|
105
|
+
...(entries.length === 0
|
|
106
|
+
? ["- none"]
|
|
107
|
+
: entries.map(({ directory, projectId }) => `- ${directory} (${projectId})`))
|
|
108
|
+
];
|
|
109
|
+
return [
|
|
110
|
+
`WorkItem: ${context.workItem.id}`,
|
|
111
|
+
`Workspace root: ${context.workspace.root}`,
|
|
112
|
+
...projectLines("Writable Projects", writable),
|
|
113
|
+
...projectLines("Context-only Projects", contextOnly),
|
|
114
|
+
`Read the full Task state with \`yui task context ${context.taskId}\`.`,
|
|
115
|
+
`Read the current WorkItem scope with \`yui task work list ${context.taskId}\`.`,
|
|
116
|
+
"Modify only Writable Projects. If another Project must change, stop and ask "
|
|
117
|
+
+ "the Task Leader to expand this WorkItem scope before continuing."
|
|
118
|
+
];
|
|
67
119
|
}
|
|
68
120
|
function requireText(value, label) {
|
|
69
121
|
const normalized = value.trim();
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { SYSTEM_LEADER_ROLE, SYSTEM_OPERATOR_ROLE } from "../role/systemRoles.js";
|
|
6
|
+
const BUILTIN_YUI_SKILLS = new Set([
|
|
7
|
+
"yui-operator",
|
|
8
|
+
"yui-leader",
|
|
9
|
+
"yui-worker",
|
|
10
|
+
"yui-reviewer"
|
|
11
|
+
]);
|
|
12
|
+
/**
|
|
13
|
+
* Compiles stable Role policy for an Agent-native instruction channel.
|
|
14
|
+
* Dynamic wakeups and Run assignments deliberately remain outside this value.
|
|
15
|
+
*/
|
|
16
|
+
export function compileRoleSessionContext(yuiHome, role, owner, options = {}) {
|
|
17
|
+
const kind = roleSessionKind(role, owner, options.purpose ?? "execution");
|
|
18
|
+
const builtInSkillId = kind === "global" ? undefined : `yui-${kind}`;
|
|
19
|
+
const skillIds = unique([
|
|
20
|
+
...(builtInSkillId === undefined ? [] : [builtInSkillId]),
|
|
21
|
+
...(role.skills ?? [])
|
|
22
|
+
]);
|
|
23
|
+
const skills = loadYuiSkillContexts(yuiHome, skillIds);
|
|
24
|
+
return {
|
|
25
|
+
developerInstructions: renderDeveloperInstructions(kind, role, owner),
|
|
26
|
+
skills,
|
|
27
|
+
...(yuiHome === undefined
|
|
28
|
+
? {}
|
|
29
|
+
: { managedContextFile: roleContextFile(yuiHome, role, owner, kind) })
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function renderDeveloperInstructions(kind, role, owner) {
|
|
33
|
+
const core = renderRoleCore(kind, role, owner);
|
|
34
|
+
const profile = [
|
|
35
|
+
role.description === undefined ? null : `Role description: ${role.description}`,
|
|
36
|
+
...(role.responsibilities ?? []).map((value) => `Role responsibility: ${value}`),
|
|
37
|
+
...(role.constraints ?? []).map((value) => `Role constraint: ${value}`),
|
|
38
|
+
role.expectedOutput === undefined ? null : `Expected output: ${role.expectedOutput}`,
|
|
39
|
+
role.systemPrompt === undefined ? null : `Additional Role instructions:\n${role.systemPrompt}`
|
|
40
|
+
].filter((value) => value !== null);
|
|
41
|
+
return [...core, ...profile].join("\n");
|
|
42
|
+
}
|
|
43
|
+
function roleSessionKind(role, owner, purpose) {
|
|
44
|
+
if (owner.scope === "global") {
|
|
45
|
+
return role.name === SYSTEM_OPERATOR_ROLE ? "operator" : "global";
|
|
46
|
+
}
|
|
47
|
+
if (purpose === "review")
|
|
48
|
+
return "reviewer";
|
|
49
|
+
return role.name === SYSTEM_LEADER_ROLE ? "leader" : "worker";
|
|
50
|
+
}
|
|
51
|
+
function renderRoleCore(kind, role, owner) {
|
|
52
|
+
switch (kind) {
|
|
53
|
+
case "operator":
|
|
54
|
+
return [
|
|
55
|
+
"You are the global Yui Operator and the user's CLI proxy.",
|
|
56
|
+
"Manage Yui through its CLI; do not perform Task implementation work.",
|
|
57
|
+
"Follow the injected yui-operator Skill when coordinating Yui."
|
|
58
|
+
];
|
|
59
|
+
case "global":
|
|
60
|
+
return [
|
|
61
|
+
`You are global Yui Role ${role.name}.`,
|
|
62
|
+
"Follow the configured Role profile and the user's instructions."
|
|
63
|
+
];
|
|
64
|
+
case "leader":
|
|
65
|
+
return [
|
|
66
|
+
`You are the Yui Leader for Task ${owner.scope === "task" ? owner.taskId : role.name}.`,
|
|
67
|
+
"Own Task stewardship and delegate bounded implementation work.",
|
|
68
|
+
"Follow the injected yui-leader Skill for Yui coordination."
|
|
69
|
+
];
|
|
70
|
+
case "reviewer":
|
|
71
|
+
return [
|
|
72
|
+
`You are Yui review Role ${role.name} for Task ${owner.scope === "task" ? owner.taskId : role.name}.`,
|
|
73
|
+
"Review only the exact frozen ReviewRound scope assigned to this Run.",
|
|
74
|
+
"Follow the injected yui-reviewer Skill while reviewing Yui work."
|
|
75
|
+
];
|
|
76
|
+
case "worker":
|
|
77
|
+
return [
|
|
78
|
+
`You are Yui Role ${role.name}${owner.scope === "task" ? ` for Task ${owner.taskId}` : ""}.`,
|
|
79
|
+
"Execute only the work delegated to this Role and report through Yui.",
|
|
80
|
+
"Follow the injected yui-worker Skill while handling Yui work."
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export function loadYuiSkillContexts(yuiHome, skillIds) {
|
|
85
|
+
return unique(skillIds).map((id) => loadSkill(yuiHome, id, BUILTIN_YUI_SKILLS.has(id)));
|
|
86
|
+
}
|
|
87
|
+
function loadSkill(yuiHome, id, builtIn) {
|
|
88
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(id)) {
|
|
89
|
+
throw new Error(`Invalid configured Skill id: ${id}.`);
|
|
90
|
+
}
|
|
91
|
+
if (!builtIn && yuiHome === undefined) {
|
|
92
|
+
throw new Error("YUI_HOME is required to load configured Role skills.");
|
|
93
|
+
}
|
|
94
|
+
const path = builtIn
|
|
95
|
+
? resolve(fileURLToPath(new URL(`../../skills/${id}/`, import.meta.url)))
|
|
96
|
+
: resolve(join(yuiHome, "skills", id));
|
|
97
|
+
try {
|
|
98
|
+
return { id, path, content: readFileSync(join(path, "SKILL.md"), "utf8").trim() };
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
102
|
+
throw new Error(`Configured Skill not found: ${id}.`);
|
|
103
|
+
}
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function unique(values) {
|
|
108
|
+
return [...new Set(values)];
|
|
109
|
+
}
|
|
110
|
+
function roleContextFile(yuiHome, role, owner, kind) {
|
|
111
|
+
const identity = createHash("sha256").update(JSON.stringify([
|
|
112
|
+
owner.scope,
|
|
113
|
+
...(owner.scope === "task" ? [owner.taskId] : []),
|
|
114
|
+
kind,
|
|
115
|
+
role.name,
|
|
116
|
+
role.activeAgentId
|
|
117
|
+
])).digest("hex");
|
|
118
|
+
return resolve(join(yuiHome, "runtime", "session-contexts", `${identity}.md`));
|
|
119
|
+
}
|