@zq-silk/yui 2.2.0 → 2.4.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/README.md +19 -3
- package/dist/artifacts/artifactCapability.js +39 -10
- package/dist/artifacts/imageMetadata.js +120 -0
- package/dist/artifacts/managedGit.js +7 -4
- package/dist/artifacts/taskArtifactRepository.js +6 -0
- package/dist/cli/commandCatalog.js +25 -11
- package/dist/cli/interactionPolicy.js +4 -0
- package/dist/cli/invocationAuthority.js +1 -1
- package/dist/commands/executionAuditCommands.js +5 -0
- package/dist/commands/globalRoleCommands.js +41 -3
- package/dist/commands/projectCommands.js +89 -94
- package/dist/commands/roleSkillValidation.js +4 -8
- package/dist/commands/taskCommands.js +30 -9
- package/dist/commands/taskIntegrationCommands.js +5 -16
- package/dist/commands/taskUsageCommand.js +38 -0
- package/dist/context/roleSessionContext.js +18 -2
- package/dist/context/runContextPack.js +5 -1
- package/dist/context/sessionBootstrapManifest.js +10 -1
- package/dist/context/skillCatalog.js +108 -0
- package/dist/context/skillPackage.js +161 -0
- package/dist/context/taskContext.js +3 -2
- package/dist/context/taskSearch.js +127 -0
- package/dist/controlPlaneCli.js +51 -39
- package/dist/controller/clientRuntime.js +3 -2
- package/dist/controller/fileSchedulerStoreAdapter.js +32 -6
- package/dist/controller/jobControl.js +2 -0
- package/dist/controller/nativeHostInput.js +37 -0
- package/dist/controller/runtime.js +93 -2
- package/dist/controller/runtimeEventInbox.js +2 -2
- package/dist/controller/runtimeEventProcessor.js +3 -3
- package/dist/doctor/doctor.js +10 -0
- package/dist/execution/reviewMainRun.js +1 -0
- package/dist/execution/workItemMainRun.js +1 -0
- package/dist/executor/codexConfigConflict.js +6 -4
- package/dist/executor/effectiveLaunch.js +24 -1
- package/dist/executor/fileRoleLaunchPlanner.js +13 -5
- package/dist/integration/gitIntegrationService.js +15 -79
- package/dist/integration/integrationSourceApplication.js +4 -2
- package/dist/job/httpPreview.js +61 -0
- package/dist/job/jobCleanupInspection.js +67 -0
- package/dist/job/jobRunner.js +12 -2
- package/dist/kernel/builtinCapabilities.js +3 -1
- package/dist/message/message.js +15 -1
- package/dist/output/rolePresentation.js +4 -1
- package/dist/repository/executionLaneGitSnapshot.js +2 -1
- package/dist/repository/gitCompatibility.js +62 -0
- package/dist/repository/gitWorkspace.js +86 -80
- package/dist/repository/gitWorktreeInventory.js +121 -0
- package/dist/repository/project.js +74 -25
- package/dist/repository/taskWorkspaceCoordinator.js +72 -145
- package/dist/repository/taskWorkspacePreparer.js +39 -43
- package/dist/repository/workspaceCleanupInspection.js +88 -7
- package/dist/resources/resourceDiscovery.js +2 -2
- package/dist/resources/resourceGc.js +7 -4
- package/dist/resources/resourceRegistrar.js +18 -11
- package/dist/runtime/agentEndpoint.js +8 -0
- package/dist/runtime/agentHost.js +170 -3
- package/dist/runtime/codexAppServerRuntime.js +1 -1
- package/dist/runtime/codexNativeAccess.js +175 -0
- package/dist/runtime/controlledCodexTui.js +116 -0
- package/dist/runtime/integrationRuntimeIsolation.js +21 -0
- package/dist/runtime/publicReply.js +23 -0
- package/dist/runtime/runtimeObservation.js +48 -0
- package/dist/runtime/structuredProviderHost.js +16 -7
- package/dist/runtime/taskCostMetrics.js +133 -0
- package/dist/runtime/taskRuntimeIsolation.js +91 -21
- package/dist/runtime/taskUsageMetrics.js +11 -2
- package/dist/runtime/taskUsageQuery.js +38 -0
- package/dist/scheduler/taskExecutionProjection.js +2 -0
- package/dist/scheduler/taskObservabilityProjection.js +9 -1
- package/dist/storage/homeLayout.js +13 -0
- package/dist/storage/sqliteSchema.js +44 -0
- package/dist/storage/sqliteStore.js +53 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +2 -0
- package/dist/storage/taskSearch.js +35 -0
- package/dist/storage/upgrade/projectKnowledgeMigration.js +52 -0
- package/dist/task/archiveDiagnostics.js +26 -8
- package/dist/task/archivePreflight.js +26 -9
- package/dist/task/leaderArchive.js +9 -15
- package/dist/task/ordinaryArchive.js +42 -0
- package/dist/tmux/tmuxManager.js +8 -8
- package/dist/web/assets/assetManifest.js +11 -51
- package/dist/web/assets/client/app/catalog.js +215 -0
- package/dist/web/assets/client/app/main.js +148 -0
- package/dist/web/assets/client/app/selection.js +139 -0
- package/dist/web/assets/client/app/settings.js +326 -0
- package/dist/web/assets/client/app/taskView.js +280 -0
- package/dist/web/assets/client/domain/context.js +96 -0
- package/dist/web/assets/client/domain/records.js +126 -0
- package/dist/web/assets/client/domain/runs.js +119 -0
- package/dist/web/assets/client/domain/taskForms.js +197 -0
- package/dist/web/assets/client/domain/vocab.js +69 -0
- package/dist/web/assets/client/domain/work.js +116 -0
- package/dist/web/assets/client/i18n/en.js +900 -0
- package/dist/web/assets/client/i18n/zh-CN.js +898 -0
- package/dist/web/assets/client/index.js +114 -0
- package/dist/web/assets/client/layout/resizer.js +37 -0
- package/dist/web/assets/client/layout/router.js +35 -0
- package/dist/web/assets/client/layout/shortcuts.js +45 -0
- package/dist/web/assets/client/layout/sizing.js +95 -0
- package/dist/web/assets/client/layout/workspace.js +247 -0
- package/dist/web/assets/client/{api.js → lib/api.js} +28 -4
- package/dist/web/assets/client/{dom.js → lib/dom.js} +24 -12
- package/dist/web/assets/client/{format.js → lib/format.js} +7 -0
- package/dist/web/assets/client/lib/i18n.js +54 -0
- package/dist/web/assets/client/lib/markdown.js +167 -0
- package/dist/web/assets/client/lib/prefs.js +28 -0
- package/dist/web/assets/client/lib/theme.js +68 -0
- package/dist/web/assets/client/ui/containers.js +101 -0
- package/dist/web/assets/client/ui/controls.js +51 -0
- package/dist/web/assets/client/ui/forms.js +48 -0
- package/dist/web/assets/client/ui/metrics.js +137 -0
- package/dist/web/assets/client/ui/primitives.js +92 -0
- package/dist/web/assets/client/ui/text.js +33 -0
- package/dist/web/assets/client/ui/toast.js +12 -0
- package/dist/web/assets/client/views/bodySearch.js +172 -0
- package/dist/web/assets/client/views/createTask.js +85 -0
- package/dist/web/assets/client/views/dock/conversation.js +612 -0
- package/dist/web/assets/client/views/dock/discussion.js +173 -0
- package/dist/web/assets/client/views/dock/preview.js +129 -0
- package/dist/web/assets/client/views/dock/terminal.js +199 -0
- package/dist/web/assets/client/views/globalInput.js +67 -0
- package/dist/web/assets/client/{overview.js → views/overview.js} +62 -56
- package/dist/web/assets/client/{sidebar.js → views/sidebar.js} +56 -62
- package/dist/web/assets/client/views/skills.js +152 -0
- package/dist/web/assets/client/views/task/actions.js +36 -0
- package/dist/web/assets/client/views/task/delivery.js +185 -0
- package/dist/web/assets/client/views/task/deliveryResults.js +159 -0
- package/dist/web/assets/client/views/task/files.js +272 -0
- package/dist/web/assets/client/views/task/observation.js +153 -0
- package/dist/web/assets/client/views/task/overview.js +161 -0
- package/dist/web/assets/client/views/task/page.js +131 -0
- package/dist/web/assets/client/views/task/records.js +163 -0
- package/dist/web/assets/client/views/task/runtime.js +130 -0
- package/dist/web/assets/client/views/task/timeline.js +190 -0
- package/dist/web/assets/shared/geometry.js +19 -0
- package/dist/web/assets/{icons.js → shared/icons.js} +4 -0
- package/dist/web/assets/shared/taskTabs.js +8 -0
- package/dist/web/assets/shared/themes.js +93 -0
- package/dist/web/assets/shell/dialogs.js +63 -0
- package/dist/web/assets/shell/dock.js +40 -0
- package/dist/web/assets/shell/index.js +40 -0
- package/dist/web/assets/shell/settings.js +34 -0
- package/dist/web/assets/shell/sidebar.js +45 -0
- package/dist/web/assets/styles/index.js +41 -0
- package/dist/web/assets/styles/{responsive.js → layout/responsive.js} +15 -13
- package/dist/web/assets/styles/{layout.js → layout/workspace.js} +17 -24
- package/dist/web/assets/styles/markdown.js +22 -3
- package/dist/web/assets/styles/tokens.js +10 -63
- package/dist/web/assets/styles/ui/controls.js +76 -0
- package/dist/web/assets/styles/ui/forms.js +46 -0
- package/dist/web/assets/styles/ui/surfaces.js +122 -0
- package/dist/web/assets/styles/views/delivery.js +29 -0
- package/dist/web/assets/styles/views/dock.js +71 -0
- package/dist/web/assets/styles/views/page.js +32 -0
- package/dist/web/assets/styles/views/records.js +70 -0
- package/dist/web/assets/styles/views/settings.js +26 -0
- package/dist/web/assets/styles/views/sidebar.js +74 -0
- package/dist/web/assets/styles/views/task.js +80 -0
- package/dist/web/assets/styles/views/terminal.js +38 -0
- package/dist/web/taskPreviews.js +205 -0
- package/dist/web/tmuxWebTerminal.js +16 -9
- package/dist/web/webConversation.js +373 -0
- package/dist/web/webMaterials.js +155 -0
- package/dist/web/webServer.js +306 -18
- package/dist/web/webSettings.js +331 -0
- package/dist/web/webSkills.js +86 -0
- package/dist/web/webSnapshot.js +40 -8
- package/dist/web/webTaskSurface.js +114 -9
- package/dist/workspace/gitChangeSetCapture.js +3 -1
- package/docs/managed-turn-and-session-runtime.md +9 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +7 -0
- package/docs/observability/README.md +13 -3
- package/docs/observability/README.zh-CN.md +10 -3
- package/docs/release-workflow.md +53 -0
- package/docs/release-workflow.zh-CN.md +40 -0
- package/docs/storage-baseline.md +38 -1
- package/docs/storage-baseline.zh-CN.md +22 -0
- package/docs/task-delivery.md +36 -8
- package/docs/task-delivery.zh-CN.md +24 -7
- package/docs/task-discovery.md +28 -0
- package/docs/testing/verification-levels.md +2 -2
- package/i18n/README.zh-CN.md +4 -3
- package/package.json +1 -1
- package/skills/yui-leader/references/execution.md +2 -1
- package/skills/yui-leader/references/integration.md +20 -0
- package/skills/yui-operator/SKILL.md +4 -0
- package/skills/yui-reviewer/SKILL.md +3 -0
- package/skills/yui-runtime/SKILL.md +4 -0
- package/skills/yui-runtime/references/publication.md +20 -4
- package/skills/yui-worker/SKILL.md +3 -0
- package/dist/web/assets/client/app.js +0 -804
- package/dist/web/assets/client/components.js +0 -418
- package/dist/web/assets/client/detail.js +0 -333
- package/dist/web/assets/client/dock.js +0 -225
- package/dist/web/assets/client/evidence.js +0 -172
- package/dist/web/assets/client/forms.js +0 -230
- package/dist/web/assets/client/i18n.js +0 -1161
- package/dist/web/assets/client/markdown.js +0 -114
- package/dist/web/assets/client/records.js +0 -63
- package/dist/web/assets/client/sections.js +0 -313
- package/dist/web/assets/client/theme.js +0 -71
- package/dist/web/assets/shell.js +0 -161
- package/dist/web/assets/styles/components.js +0 -169
- package/dist/web/assets/styles/views.js +0 -292
|
@@ -3,6 +3,7 @@ import { readdir, rename, rm } from "node:fs/promises";
|
|
|
3
3
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
4
|
import { CliError, usageError } from "../errors/cliError.js";
|
|
5
5
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
6
|
+
import { boundedDocument, readOptions, recordPage } from "../output/boundedRead.js";
|
|
6
7
|
import { healCheckoutSwap, restoreCheckoutSwap, swapManagedCheckout } from "../repository/checkoutSwap.js";
|
|
7
8
|
import { GitWorkspaceRefreshError, NodeGitWorkspace } from "../repository/gitWorkspace.js";
|
|
8
9
|
import { acquireProjectMaintenanceLock } from "../repository/projectMaintenanceLock.js";
|
|
@@ -74,17 +75,11 @@ export async function runProjectCommand(args, store, options = {}) {
|
|
|
74
75
|
}
|
|
75
76
|
if (command === "knowledge") {
|
|
76
77
|
const result = projectKnowledge(rest, store, options);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return { output: result.output, data: { project, knowledge } };
|
|
83
|
-
}
|
|
84
|
-
const knowledge = result.all
|
|
85
|
-
? project.knowledge
|
|
86
|
-
: project.knowledge.filter(({ status }) => status === "active");
|
|
87
|
-
return { output: result.output, data: { project, knowledge } };
|
|
78
|
+
return { output: result.output, data: result.data ?? {
|
|
79
|
+
projectId: result.projectId,
|
|
80
|
+
...(result.knowledgeId === undefined ? {} : { knowledgeId: result.knowledgeId }),
|
|
81
|
+
...(result.proposalId === undefined ? {} : { proposalId: result.proposalId })
|
|
82
|
+
} };
|
|
88
83
|
}
|
|
89
84
|
throw usageError(command === undefined
|
|
90
85
|
? "Project command is required."
|
|
@@ -1198,19 +1193,22 @@ function projectKnowledge(args, store, options) {
|
|
|
1198
1193
|
}
|
|
1199
1194
|
if (command === "list") {
|
|
1200
1195
|
const all = rest.includes("--all");
|
|
1201
|
-
const
|
|
1202
|
-
if (positionals.length !== 1
|
|
1203
|
-
throw usageError("Project knowledge list usage: yui project knowledge list <project> [--all].");
|
|
1204
|
-
}
|
|
1205
|
-
const project = requireProject(store, positionals[0]);
|
|
1206
|
-
const
|
|
1207
|
-
|
|
1208
|
-
:
|
|
1196
|
+
const parsed = readOptions(rest.filter((value) => value !== "--all"));
|
|
1197
|
+
if (parsed.positionals.length !== 1) {
|
|
1198
|
+
throw usageError("Project knowledge list usage: yui project knowledge list <project> [--all] [--limit <1..100>] [--cursor <cursor>].");
|
|
1199
|
+
}
|
|
1200
|
+
const project = requireProject(store, parsed.positionals[0]);
|
|
1201
|
+
const page = recordPage(project.knowledge.filter(({ status }) => all || status === "active").map(entry => ({
|
|
1202
|
+
id: entry.id, title: entry.title.slice(0, 200), version: entry.version, status: entry.status,
|
|
1203
|
+
scope: entry.scope?.slice(0, 200), expiresWhen: entry.expiresWhen?.slice(0, 200),
|
|
1204
|
+
updatedAt: entry.updatedAt
|
|
1205
|
+
})), `project:${project.id}/knowledge/${all ? "all" : "active"}`, parsed);
|
|
1206
|
+
const entries = page.items;
|
|
1209
1207
|
if (entries.length === 0) {
|
|
1210
1208
|
return {
|
|
1211
1209
|
output: "No project knowledge found.\n",
|
|
1212
1210
|
projectId: project.id,
|
|
1213
|
-
|
|
1211
|
+
data: { projectId: project.id, ...page }
|
|
1214
1212
|
};
|
|
1215
1213
|
}
|
|
1216
1214
|
return {
|
|
@@ -1218,25 +1216,32 @@ function projectKnowledge(args, store, options) {
|
|
|
1218
1216
|
{ header: "Knowledge", minWidth: 9, maxWidth: 24 },
|
|
1219
1217
|
{ header: "Title", minWidth: 8, maxWidth: 64 },
|
|
1220
1218
|
{ header: "Status", minWidth: 6, maxWidth: 10 }
|
|
1221
|
-
], entries.map((entry) => [entry.id, entry.title, entry.status]), defaultTableWidth())}\n`,
|
|
1219
|
+
], entries.map((entry) => [entry.id, entry.title, entry.status]), defaultTableWidth())}\n${page.nextCursor === null ? "" : `Next cursor: ${page.nextCursor}\n`}`,
|
|
1222
1220
|
projectId: project.id,
|
|
1223
|
-
|
|
1221
|
+
data: { projectId: project.id, ...page }
|
|
1224
1222
|
};
|
|
1225
1223
|
}
|
|
1226
1224
|
if (command === "show") {
|
|
1227
|
-
|
|
1228
|
-
|
|
1225
|
+
const parsed = readOptions(rest, ["--cursor"]);
|
|
1226
|
+
if (parsed.positionals.length !== 2) {
|
|
1227
|
+
throw usageError("Project knowledge show usage: yui project knowledge show <project> <knowledge> [--cursor <cursor>].");
|
|
1229
1228
|
}
|
|
1230
|
-
const project = requireProject(store,
|
|
1231
|
-
const entry = project.knowledge.find(({ id }) => id ===
|
|
1229
|
+
const project = requireProject(store, parsed.positionals[0]);
|
|
1230
|
+
const entry = project.knowledge.find(({ id }) => id === parsed.positionals[1]);
|
|
1232
1231
|
if (entry === undefined)
|
|
1233
|
-
throw usageError(`Project knowledge not found: ${
|
|
1232
|
+
throw usageError(`Project knowledge not found: ${parsed.positionals[1]}.`);
|
|
1233
|
+
const data = boundedDocument({ projectId: project.id, knowledge: entry }, `project:${project.id}/knowledge/${entry.id}`, parsed.cursor);
|
|
1234
|
+
if ("contentPage" in data)
|
|
1235
|
+
return { output: `${JSON.stringify(data)}\n`, projectId: project.id, data };
|
|
1234
1236
|
return {
|
|
1235
1237
|
output: [
|
|
1236
1238
|
`Knowledge: ${entry.id}`,
|
|
1237
1239
|
`Project: ${project.id}`,
|
|
1238
1240
|
`Title: ${entry.title}`,
|
|
1239
1241
|
`Status: ${entry.status}`,
|
|
1242
|
+
`Version: ${entry.version}`,
|
|
1243
|
+
...(entry.scope === undefined ? [] : [`Scope: ${entry.scope}`]),
|
|
1244
|
+
...(entry.expiresWhen === undefined ? [] : [`Expires when: ${entry.expiresWhen}`]),
|
|
1240
1245
|
...(entry.provenance === undefined ? [] : [
|
|
1241
1246
|
`Source: ${entry.provenance.taskId}`
|
|
1242
1247
|
+ `${entry.provenance.decisionId === undefined ? "" : `/${entry.provenance.decisionId}`}`
|
|
@@ -1253,10 +1258,14 @@ function projectKnowledge(args, store, options) {
|
|
|
1253
1258
|
: [`Promoted by: ${entry.provenance.promotedBy} at ${entry.provenance.promotedAt ?? "?"}`])
|
|
1254
1259
|
]),
|
|
1255
1260
|
"",
|
|
1256
|
-
entry.body
|
|
1261
|
+
entry.body,
|
|
1262
|
+
...(entry.history.length === 0 ? [] : [
|
|
1263
|
+
"", "Prior versions (exact records):", JSON.stringify(entry.history, null, 2)
|
|
1264
|
+
])
|
|
1257
1265
|
].join("\n").concat("\n"),
|
|
1258
1266
|
projectId: project.id,
|
|
1259
|
-
knowledgeId: entry.id
|
|
1267
|
+
knowledgeId: entry.id,
|
|
1268
|
+
data
|
|
1260
1269
|
};
|
|
1261
1270
|
}
|
|
1262
1271
|
if (command === "update") {
|
|
@@ -1401,7 +1410,7 @@ function parseKnowledgeProposalOptions(args, usage) {
|
|
|
1401
1410
|
* Leader-proposed promotion candidate. The proposal is workflow state, not
|
|
1402
1411
|
* Knowledge: it never appears in `project knowledge list/show` until an
|
|
1403
1412
|
* Operator accepts it. A managed Task Session may propose only for its own
|
|
1404
|
-
* Task. The same candidate (source
|
|
1413
|
+
* Task. The same candidate (source, content, applicability and replacement intent) is deduplicated to
|
|
1405
1414
|
* the existing pending proposal instead of creating a second copy.
|
|
1406
1415
|
*/
|
|
1407
1416
|
function proposeKnowledge(args, store, options) {
|
|
@@ -1430,7 +1439,10 @@ function proposeKnowledge(args, store, options) {
|
|
|
1430
1439
|
projectId: project.id,
|
|
1431
1440
|
source,
|
|
1432
1441
|
title: parsed.title,
|
|
1433
|
-
body: parsed.body
|
|
1442
|
+
body: parsed.body,
|
|
1443
|
+
...(parsed.scope === undefined ? {} : { scope: parsed.scope }),
|
|
1444
|
+
...(parsed.expiresWhen === undefined ? {} : { expiresWhen: parsed.expiresWhen }),
|
|
1445
|
+
...(parsed.supersedes === undefined ? {} : { supersedesKnowledgeId: parsed.supersedes })
|
|
1434
1446
|
});
|
|
1435
1447
|
const pending = findKnowledgeProposalByFingerprint(project, fingerprint, "pending");
|
|
1436
1448
|
if (pending !== null) {
|
|
@@ -1452,7 +1464,7 @@ function proposeKnowledge(args, store, options) {
|
|
|
1452
1464
|
};
|
|
1453
1465
|
}
|
|
1454
1466
|
const proposal = {
|
|
1455
|
-
schemaVersion:
|
|
1467
|
+
schemaVersion: 2,
|
|
1456
1468
|
id: nextProposalId(project),
|
|
1457
1469
|
projectId: project.id,
|
|
1458
1470
|
title: parsed.title,
|
|
@@ -1528,50 +1540,29 @@ function requireProposalEvidence(store, parsed) {
|
|
|
1528
1540
|
}
|
|
1529
1541
|
function listKnowledgeProposals(args, store) {
|
|
1530
1542
|
const usage = "Project knowledge proposals list usage: yui project knowledge proposals list <project>"
|
|
1531
|
-
+ " [--status pending|accepted|rejected] [--all].";
|
|
1532
|
-
const
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
if (sawStatus)
|
|
1540
|
-
throw usageError(`--all and --status are mutually exclusive. ${usage}`);
|
|
1541
|
-
sawAll = true;
|
|
1542
|
-
status = undefined;
|
|
1543
|
-
}
|
|
1544
|
-
else if (value === "--status") {
|
|
1545
|
-
if (sawAll)
|
|
1546
|
-
throw usageError(`--all and --status are mutually exclusive. ${usage}`);
|
|
1547
|
-
sawStatus = true;
|
|
1548
|
-
const next = args[index + 1];
|
|
1549
|
-
if (next === undefined || next.startsWith("--"))
|
|
1550
|
-
throw usageError(usage);
|
|
1551
|
-
if (next !== "pending" && next !== "accepted" && next !== "rejected")
|
|
1552
|
-
throw usageError(usage);
|
|
1553
|
-
status = next;
|
|
1554
|
-
index += 1;
|
|
1555
|
-
}
|
|
1556
|
-
else if (value.startsWith("--")) {
|
|
1557
|
-
throw usageError(`Unknown option: ${value}. ${usage}`);
|
|
1558
|
-
}
|
|
1559
|
-
else {
|
|
1560
|
-
positionals.push(value);
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
|
-
if (positionals.length !== 1)
|
|
1543
|
+
+ " [--status pending|accepted|rejected] [--all] [--limit <1..100>] [--cursor <cursor>].";
|
|
1544
|
+
const parsed = readOptions(args.filter(value => value !== "--all"), ["--cursor", "--limit", "--status"]);
|
|
1545
|
+
const all = args.includes("--all");
|
|
1546
|
+
if (all && parsed.values.has("--status"))
|
|
1547
|
+
throw usageError(`--all and --status are mutually exclusive. ${usage}`);
|
|
1548
|
+
const status = all ? undefined : parsed.values.get("--status") ?? "pending";
|
|
1549
|
+
if (parsed.positionals.length !== 1
|
|
1550
|
+
|| (status !== undefined && !["pending", "accepted", "rejected"].includes(status)))
|
|
1564
1551
|
throw usageError(usage);
|
|
1565
|
-
const project = requireProject(store, positionals[0]);
|
|
1566
|
-
const
|
|
1552
|
+
const project = requireProject(store, parsed.positionals[0]);
|
|
1553
|
+
const page = recordPage(project.knowledgeProposals
|
|
1567
1554
|
.filter((entry) => status === undefined || entry.status === status)
|
|
1568
1555
|
.slice()
|
|
1569
|
-
.sort((a, b) => a.id.localeCompare(b.id))
|
|
1556
|
+
.sort((a, b) => a.id.localeCompare(b.id))
|
|
1557
|
+
.map(entry => ({ id: entry.id, title: entry.title.slice(0, 200), status: entry.status,
|
|
1558
|
+
source: entry.source, knowledgeId: entry.knowledgeId,
|
|
1559
|
+
scope: entry.scope?.slice(0, 200), expiresWhen: entry.expiresWhen?.slice(0, 200) })), `project:${project.id}/knowledge-proposals/${status ?? "all"}`, parsed);
|
|
1560
|
+
const entries = page.items;
|
|
1570
1561
|
if (entries.length === 0) {
|
|
1571
1562
|
return {
|
|
1572
1563
|
output: "No knowledge proposals found.\n",
|
|
1573
1564
|
projectId: project.id,
|
|
1574
|
-
|
|
1565
|
+
data: { projectId: project.id, ...page }
|
|
1575
1566
|
};
|
|
1576
1567
|
}
|
|
1577
1568
|
return {
|
|
@@ -1589,20 +1580,24 @@ function listKnowledgeProposals(args, store) {
|
|
|
1589
1580
|
+ `${entry.source.decisionId === undefined ? "" : `/${entry.source.decisionId}`}`
|
|
1590
1581
|
+ `${entry.source.milestoneId === undefined ? "" : `/${entry.source.milestoneId}`}`,
|
|
1591
1582
|
entry.knowledgeId ?? "-"
|
|
1592
|
-
]), defaultTableWidth())}\n`,
|
|
1583
|
+
]), defaultTableWidth())}\n${page.nextCursor === null ? "" : `Next cursor: ${page.nextCursor}\n`}`,
|
|
1593
1584
|
projectId: project.id,
|
|
1594
|
-
|
|
1585
|
+
data: { projectId: project.id, ...page }
|
|
1595
1586
|
};
|
|
1596
1587
|
}
|
|
1597
1588
|
function showKnowledgeProposal(args, store) {
|
|
1598
|
-
const usage = "Project knowledge proposals show usage: yui project knowledge proposals show <project> <proposal
|
|
1599
|
-
|
|
1589
|
+
const usage = "Project knowledge proposals show usage: yui project knowledge proposals show <project> <proposal> [--cursor <cursor>].";
|
|
1590
|
+
const parsed = readOptions(args, ["--cursor"]);
|
|
1591
|
+
if (parsed.positionals.length !== 2)
|
|
1600
1592
|
throw usageError(usage);
|
|
1601
|
-
const project = requireProject(store,
|
|
1602
|
-
const proposal = findKnowledgeProposal(project,
|
|
1593
|
+
const project = requireProject(store, parsed.positionals[0]);
|
|
1594
|
+
const proposal = findKnowledgeProposal(project, parsed.positionals[1]);
|
|
1603
1595
|
if (proposal === null) {
|
|
1604
|
-
throw usageError(`Knowledge proposal not found: ${
|
|
1596
|
+
throw usageError(`Knowledge proposal not found: ${parsed.positionals[1]}.`);
|
|
1605
1597
|
}
|
|
1598
|
+
const data = boundedDocument({ projectId: project.id, proposal }, `project:${project.id}/knowledge-proposals/${proposal.id}`, parsed.cursor);
|
|
1599
|
+
if ("contentPage" in data)
|
|
1600
|
+
return { output: `${JSON.stringify(data)}\n`, projectId: project.id, data };
|
|
1606
1601
|
const lines = [
|
|
1607
1602
|
`Proposal: ${proposal.id}`,
|
|
1608
1603
|
`Project: ${project.id}`,
|
|
@@ -1629,7 +1624,7 @@ function showKnowledgeProposal(args, store) {
|
|
|
1629
1624
|
output: `${lines.join("\n")}\n`,
|
|
1630
1625
|
projectId: project.id,
|
|
1631
1626
|
proposalId: proposal.id,
|
|
1632
|
-
|
|
1627
|
+
data
|
|
1633
1628
|
};
|
|
1634
1629
|
}
|
|
1635
1630
|
/**
|
|
@@ -1654,6 +1649,10 @@ function acceptKnowledgeProposal(args, store, options) {
|
|
|
1654
1649
|
if (proposal === null) {
|
|
1655
1650
|
throw usageError(`Knowledge proposal not found: ${parsed.proposal}.`);
|
|
1656
1651
|
}
|
|
1652
|
+
if (proposal.status === "accepted"
|
|
1653
|
+
&& (parsed.update === undefined || parsed.update === proposal.knowledgeId)) {
|
|
1654
|
+
return { projectId: project.id, proposalId: proposal.id, knowledgeId: proposal.knowledgeId, duplicate: true };
|
|
1655
|
+
}
|
|
1657
1656
|
if (proposal.status !== "pending") {
|
|
1658
1657
|
throw usageError(`Knowledge proposal is already ${proposal.status}: ${proposal.id}.`);
|
|
1659
1658
|
}
|
|
@@ -1675,20 +1674,24 @@ function acceptKnowledgeProposal(args, store, options) {
|
|
|
1675
1674
|
|| previousProposal.status !== "accepted"
|
|
1676
1675
|
|| previousProposal.knowledgeId !== target.id
|
|
1677
1676
|
|| previousProposal.title !== target.title
|
|
1678
|
-
|| previousProposal.body !== target.body
|
|
1677
|
+
|| previousProposal.body !== target.body
|
|
1678
|
+
|| previousProposal.scope !== target.scope
|
|
1679
|
+
|| previousProposal.expiresWhen !== target.expiresWhen) {
|
|
1679
1680
|
throw usageError(`Knowledge ${target.id} has no complete proposal-backed version history. `
|
|
1680
1681
|
+ "Create the replacement proposal with --supersedes instead of --update.");
|
|
1681
1682
|
}
|
|
1682
1683
|
knowledgeId = target.id;
|
|
1683
1684
|
next = updateProjectKnowledge(next, knowledgeId, {
|
|
1684
1685
|
title: proposal.title,
|
|
1685
|
-
body: proposal.body
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1686
|
+
body: proposal.body,
|
|
1687
|
+
scope: proposal.scope ?? null,
|
|
1688
|
+
expiresWhen: proposal.expiresWhen ?? null,
|
|
1689
|
+
provenance: {
|
|
1690
|
+
...provenanceFromProposal(proposal),
|
|
1691
|
+
proposalId: proposal.id,
|
|
1692
|
+
promotedBy: decidedBy,
|
|
1693
|
+
promotedAt: now.toISOString()
|
|
1694
|
+
}
|
|
1692
1695
|
}, now);
|
|
1693
1696
|
}
|
|
1694
1697
|
else {
|
|
@@ -1707,7 +1710,7 @@ function acceptKnowledgeProposal(args, store, options) {
|
|
|
1707
1710
|
proposalId: proposal.id,
|
|
1708
1711
|
promotedBy: decidedBy,
|
|
1709
1712
|
promotedAt: now.toISOString()
|
|
1710
|
-
});
|
|
1713
|
+
}, proposal);
|
|
1711
1714
|
}
|
|
1712
1715
|
}
|
|
1713
1716
|
next = decideKnowledgeProposal(next, proposal.id, {
|
|
@@ -1768,14 +1771,6 @@ function provenanceFromProposal(proposal) {
|
|
|
1768
1771
|
fingerprint: proposal.fingerprint
|
|
1769
1772
|
};
|
|
1770
1773
|
}
|
|
1771
|
-
/** Replace a Knowledge entry's provenance (used by accept --update). */
|
|
1772
|
-
function withKnowledgeProvenance(project, knowledgeId, provenance, now) {
|
|
1773
|
-
const timestamp = now.toISOString();
|
|
1774
|
-
const knowledge = project.knowledge.map((entry) => (entry.id === knowledgeId
|
|
1775
|
-
? { ...entry, provenance, updatedAt: timestamp }
|
|
1776
|
-
: entry));
|
|
1777
|
-
return validateProject({ ...project, knowledge, updatedAt: timestamp });
|
|
1778
|
-
}
|
|
1779
1774
|
function parseAcceptArguments(args, usage) {
|
|
1780
1775
|
const positionals = [];
|
|
1781
1776
|
let update;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { accessSync, constants, statSync } from "node:fs";
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
1
|
import { usageError } from "../errors/cliError.js";
|
|
2
|
+
import { loadYuiSkillContexts } from "../context/roleSessionContext.js";
|
|
4
3
|
const ROLE_LAUNCH_CONTEXT_OPTIONS = new Set([
|
|
5
4
|
"--workspace",
|
|
6
5
|
"--description",
|
|
@@ -34,14 +33,11 @@ export function validateConfiguredRoleSkills(yuiHome, skillIds) {
|
|
|
34
33
|
if (!SAFE_SKILL_ID.test(id)) {
|
|
35
34
|
throw usageError(`Configured Skill id is invalid: ${id}.`);
|
|
36
35
|
}
|
|
37
|
-
const path = resolve(yuiHome, "skills", id, "SKILL.md");
|
|
38
36
|
try {
|
|
39
|
-
|
|
40
|
-
throw new Error("not a file");
|
|
41
|
-
accessSync(path, constants.R_OK);
|
|
37
|
+
loadYuiSkillContexts(yuiHome, [id]);
|
|
42
38
|
}
|
|
43
|
-
catch {
|
|
44
|
-
throw usageError(`Configured Skill not found or
|
|
39
|
+
catch (error) {
|
|
40
|
+
throw usageError(`Configured Skill not found, unreadable or invalid: ${id}. ${error instanceof Error ? error.message : ""}`);
|
|
45
41
|
}
|
|
46
42
|
}
|
|
47
43
|
}
|
|
@@ -10,6 +10,7 @@ import { buildRunContextDelta, buildRunContextPack, contextSnapshotDeltaRefIds,
|
|
|
10
10
|
import { createRunInput } from "../context/runInputContract.js";
|
|
11
11
|
import { parseTaskCatalogOptions, readTaskCatalog, renderTaskCatalog } from "../context/taskCatalog.js";
|
|
12
12
|
import { assertContextRecordReadable, readTaskContextResource } from "../context/taskContext.js";
|
|
13
|
+
import { parseTaskSearchOptions, searchTaskBodies } from "../context/taskSearch.js";
|
|
13
14
|
import { boundedDocument, readOptions, messageReceipt, recordPage } from "../output/boundedRead.js";
|
|
14
15
|
import { referencedWakeRunIds } from "../context/wakeRunReferences.js";
|
|
15
16
|
import { completeProcessing, releaseProcessing } from "../coordination/workMailbox.js";
|
|
@@ -55,7 +56,7 @@ import { queueLeaderWakeup } from "../scheduler/wakeupQueue.js";
|
|
|
55
56
|
import { planningRuntimeCwd } from "../storage/homeLayout.js";
|
|
56
57
|
import { StorageConflictError } from "../storage/taskStore.js";
|
|
57
58
|
import { archiveDeliveryWarnings, archiveRetainedResources, renderArchiveDiagnostics, taskArchiveDiagnostics } from "../task/archiveDiagnostics.js";
|
|
58
|
-
import { archiveSettlementChecks } from "../task/archivePreflight.js";
|
|
59
|
+
import { archiveExecutionChecks, archiveSettlementChecks } from "../task/archivePreflight.js";
|
|
59
60
|
import { projectCompletionReadiness } from "../task/completionReadiness.js";
|
|
60
61
|
import { assertDraftTaskExecutionFree, validateDraftWorkItemEdit } from "../task/draftPlan.js";
|
|
61
62
|
import { assertTaskRemoteDeliveryProof, projectTaskRemoteDeliveryFromStore } from "../task/remoteDeliveryService.js";
|
|
@@ -80,6 +81,7 @@ import { assertTaskOpen, clock, exactPositionals, leaderActionEventPayload, lead
|
|
|
80
81
|
import { assertTaskCompletionPublishedTreeProof } from "./taskCompletionGate.js";
|
|
81
82
|
import { runTaskContextCommand, taskRecordList } from "./taskContextCommand.js";
|
|
82
83
|
import { taskBriefCommand, taskDecisionCommand, taskEventCommand, taskMilestoneCommand } from "./taskFactCommands.js";
|
|
84
|
+
import { taskUsageCommand } from "./taskUsageCommand.js";
|
|
83
85
|
import { openInputRequestCount, runTaskInputCommand } from "./taskInputCommands.js";
|
|
84
86
|
import { runTaskNextActionCommand } from "./taskNextActionCommand.js";
|
|
85
87
|
import { runPublicationCommand } from "./taskPublicationCommands.js";
|
|
@@ -341,6 +343,10 @@ function executeTaskCommand(args, store, options = {}) {
|
|
|
341
343
|
case "create": return createTaskCommand(rest, store, options);
|
|
342
344
|
case "update": return output(updateTaskCommand(rest, store, options));
|
|
343
345
|
case "list": return listTaskCommand(rest, store, options.environment);
|
|
346
|
+
case "search": {
|
|
347
|
+
const result = searchTaskBodies(store, parseTaskSearchOptions(rest), options.environment);
|
|
348
|
+
return output(`${JSON.stringify(result, null, 2)}\n`, result);
|
|
349
|
+
}
|
|
344
350
|
case "show": return showTaskCommand(rest, store, options.actualTaskReviewCandidate ?? null);
|
|
345
351
|
case "context": return runTaskContextCommand(rest, store, options.environment);
|
|
346
352
|
case "next-action": return runTaskNextActionCommand(rest, store, options.actualTaskReviewCandidate ?? null);
|
|
@@ -368,6 +374,7 @@ function executeTaskCommand(args, store, options = {}) {
|
|
|
368
374
|
case "review": return taskReviewCommand(rest, store, options);
|
|
369
375
|
case "run": return taskRunCommand(rest, store, options);
|
|
370
376
|
case "brief": return taskBriefCommand(rest, store, options);
|
|
377
|
+
case "usage": return taskUsageCommand(rest, store, options);
|
|
371
378
|
case "decision": return taskDecisionCommand(rest, store, options);
|
|
372
379
|
case "milestone": return taskMilestoneCommand(rest, store, options);
|
|
373
380
|
case "event": return taskEventCommand(rest, store, options);
|
|
@@ -1300,12 +1307,9 @@ function archiveTaskCommand(args, store, options) {
|
|
|
1300
1307
|
? assertTaskRemoteDeliveryProof(tx, task, options.archiveRemoteDeliveryProof)
|
|
1301
1308
|
: undefined;
|
|
1302
1309
|
if (!request.force) {
|
|
1303
|
-
const checks = archiveSettlementChecks(tx, task);
|
|
1310
|
+
const checks = [...archiveSettlementChecks(tx, task), ...archiveExecutionChecks(tx, task.id, options.yuiHome)];
|
|
1304
1311
|
if (checks.length > 0)
|
|
1305
1312
|
throw new CleanupInspectionError(checks);
|
|
1306
|
-
if (task.cwd !== undefined || tx.listManagedWorkspaces(task.id).length > 0) {
|
|
1307
|
-
throw usageError(`Task ${task.id} still has managed worktrees; clean them before archiving.`);
|
|
1308
|
-
}
|
|
1309
1313
|
const activeRole = tx.listRoles(task.id)
|
|
1310
1314
|
.find((role) => tx.getActiveRun(task.id, role.name) !== null);
|
|
1311
1315
|
if (activeRole !== undefined) {
|
|
@@ -1322,7 +1326,7 @@ function archiveTaskCommand(args, store, options) {
|
|
|
1322
1326
|
+ "stop that Session before archiving.");
|
|
1323
1327
|
}
|
|
1324
1328
|
}
|
|
1325
|
-
const retainedResources =
|
|
1329
|
+
const retainedResources = archiveRetainedResources(tx, task);
|
|
1326
1330
|
const warnings = request.force && remoteDelivery !== undefined ? archiveDeliveryWarnings(remoteDelivery) : [];
|
|
1327
1331
|
const archived = { ...archiveTask(task, now, { by: actor }), executionGate: { state: "stopped" } };
|
|
1328
1332
|
tx.saveTask(archived);
|
|
@@ -1351,8 +1355,9 @@ function archiveTaskCommand(args, store, options) {
|
|
|
1351
1355
|
authorizationSource: JSON.stringify(options.archiveLeaderAdmission.source)
|
|
1352
1356
|
}),
|
|
1353
1357
|
workspaceDisposition: request.disposition,
|
|
1358
|
+
cleanup: "pending",
|
|
1354
1359
|
...(request.force ? {
|
|
1355
|
-
force: "true",
|
|
1360
|
+
force: "true",
|
|
1356
1361
|
warnings: JSON.stringify(warnings),
|
|
1357
1362
|
retainedResources: JSON.stringify(retainedResources)
|
|
1358
1363
|
} : {}),
|
|
@@ -1722,6 +1727,13 @@ function taskMessageCommand(args, store, options) {
|
|
|
1722
1727
|
* input (decision-3 §5/§6).
|
|
1723
1728
|
*/
|
|
1724
1729
|
export function applyTaskInputControl(taskId, input, store, options) {
|
|
1730
|
+
if (input.expectedSessionId !== undefined) {
|
|
1731
|
+
const roleName = input.action === "interrupt" ? input.role : input.to ?? "leader";
|
|
1732
|
+
const set = store.getTaskRoleSessionSet(taskId, roleName);
|
|
1733
|
+
if (set?.sessions[set.activeAgentId]?.nativeSessionId !== input.expectedSessionId) {
|
|
1734
|
+
throw usageError("Selected native Session changed; refresh before sending.");
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1725
1737
|
if (input.action === "queue")
|
|
1726
1738
|
return queueTaskInput(taskId, input, store, options);
|
|
1727
1739
|
if (input.action === "steer")
|
|
@@ -1747,7 +1759,8 @@ function queueTaskInput(taskId, input, store, options) {
|
|
|
1747
1759
|
if (recipientRole === undefined && (workItemId !== undefined || reviewRoundId !== undefined)) {
|
|
1748
1760
|
throw usageError("--to is required for scoped Message delivery.");
|
|
1749
1761
|
}
|
|
1750
|
-
const result = sendTaskMessageCommand(store, taskId, body, options, recipientRole === undefined ? undefined : { roleName: recipientRole, workItemId, reviewRoundId }, undefined, undefined, { action: "queue", requestId
|
|
1762
|
+
const result = sendTaskMessageCommand(store, taskId, body, options, recipientRole === undefined ? undefined : { roleName: recipientRole, workItemId, reviewRoundId }, undefined, undefined, { action: "queue", requestId,
|
|
1763
|
+
...(input.expectedSessionId === undefined ? {} : { expectedSessionId: input.expectedSessionId }) });
|
|
1751
1764
|
const reason = result.message.continuation?.notDeliveredReason;
|
|
1752
1765
|
const delivery = result.idempotentReplay ? { state: "idempotent-replay" }
|
|
1753
1766
|
: reason !== undefined ? { state: "not-delivered", reason }
|
|
@@ -1796,7 +1809,8 @@ function steerTaskInput(taskId, input, store, options) {
|
|
|
1796
1809
|
throw usageError("Steering the Leader targets its current turn directly; it takes no --work-item/--review-round Assignment.");
|
|
1797
1810
|
}
|
|
1798
1811
|
const result = leaderTarget
|
|
1799
|
-
? sendTaskMessageCommand(store, taskId, body, options, undefined, undefined, undefined, { action: "steer", requestId, expectedTarget
|
|
1812
|
+
? sendTaskMessageCommand(store, taskId, body, options, undefined, undefined, undefined, { action: "steer", requestId, expectedTarget,
|
|
1813
|
+
...(input.expectedSessionId === undefined ? {} : { expectedSessionId: input.expectedSessionId }) })
|
|
1800
1814
|
: sendTaskMessageCommand(store, taskId, body, options, { roleName: recipientRole, workItemId, reviewRoundId }, undefined, undefined, { action: "steer", requestId, expectedTarget });
|
|
1801
1815
|
const roleName = result.message.recipient?.roleName ?? recipientRole;
|
|
1802
1816
|
if (result.idempotentReplay) {
|
|
@@ -1923,6 +1937,7 @@ export function sendTaskMessageCommand(store, taskId, body, options = {}, recipi
|
|
|
1923
1937
|
: { roleName: recipient.roleName, workItemId: recipient.workItemId, reviewRoundId: recipient.reviewRoundId };
|
|
1924
1938
|
if (prior?.action !== inputControl.action || existing.body !== body
|
|
1925
1939
|
|| prior.expectedTarget !== inputControl.expectedTarget
|
|
1940
|
+
|| prior.expectedSessionId !== inputControl.expectedSessionId
|
|
1926
1941
|
|| !isDeepStrictEqual(priorRecipient, nextRecipient)) {
|
|
1927
1942
|
throw usageError(`Input requestId ${inputControl.requestId} was already used with different content or target; use a new requestId for a new input.`);
|
|
1928
1943
|
}
|
|
@@ -3521,6 +3536,7 @@ function dispatchWork(args, store, options) {
|
|
|
3521
3536
|
throw usageError(`${task.id}/${role.name} already has an active run.`);
|
|
3522
3537
|
}
|
|
3523
3538
|
const effective = resolveEffectiveLaunch({
|
|
3539
|
+
store: tx,
|
|
3524
3540
|
role,
|
|
3525
3541
|
purpose: "execution",
|
|
3526
3542
|
workspace,
|
|
@@ -3577,6 +3593,7 @@ function dispatchWork(args, store, options) {
|
|
|
3577
3593
|
}
|
|
3578
3594
|
const laneWorkspace = managedWorkspace ?? workspace;
|
|
3579
3595
|
const effective = resolveEffectiveLaunch({
|
|
3596
|
+
store: tx,
|
|
3580
3597
|
role,
|
|
3581
3598
|
purpose: "execution",
|
|
3582
3599
|
workspace: laneWorkspace,
|
|
@@ -5103,6 +5120,7 @@ function retryRunOperation(args, store, options) {
|
|
|
5103
5120
|
?? (directSession === undefined ? undefined
|
|
5104
5121
|
: effectiveLaunchWithTaskMainWorkspace(directSession.effective, retryManagedWorkspace))
|
|
5105
5122
|
?? resolveEffectiveLaunch({
|
|
5123
|
+
store: tx,
|
|
5106
5124
|
role,
|
|
5107
5125
|
purpose: previous.purpose,
|
|
5108
5126
|
...(retryManagedWorkspace === undefined ? {} : { workspace: retryManagedWorkspace }),
|
|
@@ -5638,6 +5656,7 @@ function retryFailedReviewRun(previous, store, options, now) {
|
|
|
5638
5656
|
}
|
|
5639
5657
|
selectedReviewSynthesisProducers(tx, round, group, synthesisSourceRunIds(tx, run));
|
|
5640
5658
|
const effective = resolveEffectiveLaunch({
|
|
5659
|
+
store: tx,
|
|
5641
5660
|
role: reviewer,
|
|
5642
5661
|
purpose: "review",
|
|
5643
5662
|
workspace: run.workspace,
|
|
@@ -6131,6 +6150,7 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
|
|
|
6131
6150
|
}
|
|
6132
6151
|
const runId = tx.nextRunId(taskId);
|
|
6133
6152
|
const effective = resolveEffectiveLaunch({
|
|
6153
|
+
store: tx,
|
|
6134
6154
|
role: reviewer,
|
|
6135
6155
|
purpose: "review",
|
|
6136
6156
|
workspace: round.workspace,
|
|
@@ -6193,6 +6213,7 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
|
|
|
6193
6213
|
}
|
|
6194
6214
|
const laneManagedWorkspace = preparedLaneWorkspaces.get(lane.id);
|
|
6195
6215
|
const effective = lane.effective ?? resolveEffectiveLaunch({
|
|
6216
|
+
store: tx,
|
|
6196
6217
|
role: laneReviewer,
|
|
6197
6218
|
purpose: "review",
|
|
6198
6219
|
workspace: laneManagedWorkspace,
|
|
@@ -49,19 +49,8 @@ async function cleanupIntegration(args, store, home, environment) {
|
|
|
49
49
|
&& integration.status !== "failed") {
|
|
50
50
|
throw usageError(`Integration is not terminal: ${integration.id}/${integration.status}.`);
|
|
51
51
|
}
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
// running, and unacknowledged unknown-needs-attention jobs owned by it.
|
|
55
|
-
const activeIntegrationJob = store.listDurableJobs(integration.taskId).find((job) => (job.owner.kind === "integration-attempt"
|
|
56
|
-
&& job.owner.integrationAttemptId === integration.id
|
|
57
|
-
&& (job.status === "queued"
|
|
58
|
-
|| job.status === "running"
|
|
59
|
-
|| (job.status === "unknown-needs-attention" && job.acknowledgedAt === undefined))));
|
|
60
|
-
if (activeIntegrationJob !== undefined) {
|
|
61
|
-
throw usageError(`Integration ${integration.id} has an active DurableJob: `
|
|
62
|
-
+ `${activeIntegrationJob.id}/${activeIntegrationJob.status}. `
|
|
63
|
-
+ "Cancel or acknowledge it before cleanup.");
|
|
64
|
-
}
|
|
52
|
+
// The service's shared owner inspection checks both Job settlement and
|
|
53
|
+
// current physical occupancy before any runtime or worktree is released.
|
|
65
54
|
const result = await new GitIntegrationService(home, store).cleanup(integration, {
|
|
66
55
|
authorize: () => taskLocalActor(store, environment, integration.taskId)
|
|
67
56
|
});
|
|
@@ -70,9 +59,9 @@ async function cleanupIntegration(args, store, home, environment) {
|
|
|
70
59
|
}
|
|
71
60
|
return {
|
|
72
61
|
output: result === "removed"
|
|
73
|
-
? `Cleaned Integration worktree and check logs
|
|
74
|
-
: `Integration worktree
|
|
75
|
-
data: { integrationId: integration.id, cleanup: result }
|
|
62
|
+
? `Cleaned Integration worktree ${integration.id}; history and check logs retained\n`
|
|
63
|
+
: `Integration worktree already clean: ${integration.id}; history and check logs retained\n`,
|
|
64
|
+
data: { integrationId: integration.id, cleanup: result, historyRetained: true }
|
|
76
65
|
};
|
|
77
66
|
}
|
|
78
67
|
async function start(args, store, home, now, options) {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { assertContextRecordReadable } from "../context/taskContext.js";
|
|
2
|
+
import { readTaskUsage } from "../runtime/taskUsageQuery.js";
|
|
3
|
+
import { formatUsageMetric } from "../runtime/taskUsageMetrics.js";
|
|
4
|
+
import { exactPositionals, output, parseTail, requireTask } from "./taskCommandSupport.js";
|
|
5
|
+
import { usageError } from "../errors/cliError.js";
|
|
6
|
+
import { taskActor } from "../task/taskAuthority.js";
|
|
7
|
+
export function taskUsageCommand(args, store, options) {
|
|
8
|
+
const usage = "yui task usage <task> [--limit <0..50>] [--offset <n>]";
|
|
9
|
+
const parsed = parseTail(args, new Set(["--limit", "--offset"]), usage);
|
|
10
|
+
exactPositionals(parsed.positionals, 1, usage);
|
|
11
|
+
const task = requireTask(store, parsed.positionals[0]);
|
|
12
|
+
// Task-lifetime aggregation spans every Role, beyond a Worker/Reviewer
|
|
13
|
+
// Assignment. Reading the Task record alone does not authorize these facts.
|
|
14
|
+
taskActor(options.environment, task.id);
|
|
15
|
+
assertContextRecordReadable(store, task.id, "task", task.id, options.environment);
|
|
16
|
+
const limit = Number(parsed.options.get("--limit") ?? 0), offset = Number(parsed.options.get("--offset") ?? 0);
|
|
17
|
+
if (!Number.isSafeInteger(limit) || limit < 0 || limit > 50 || !Number.isSafeInteger(offset) || offset < 0) {
|
|
18
|
+
throw usageError(usage);
|
|
19
|
+
}
|
|
20
|
+
const result = readTaskUsage(store, task.id, { limit, offset, now: options.now?.() });
|
|
21
|
+
const money = (kind) => {
|
|
22
|
+
const metric = result.costs[kind];
|
|
23
|
+
return metric.amounts.length === 0 ? `unknown (${metric.reasons.join(", ")})`
|
|
24
|
+
: metric.amounts.map(amount => `${formatUsageMetric(amount)} ${amount.currency}`).join("; ");
|
|
25
|
+
};
|
|
26
|
+
return output([
|
|
27
|
+
`Task: ${task.id} — task lifetime, observed sources only; not a bill`,
|
|
28
|
+
`Tokens: ${formatUsageMetric(result.tokens)}`,
|
|
29
|
+
`Tool calls: ${formatUsageMetric(result.toolCalls)}`,
|
|
30
|
+
`Elapsed: ${formatUsageMetric(result.elapsedSeconds, "s")}`,
|
|
31
|
+
`Native execution: ${formatUsageMetric(result.executionSeconds, "s")} (not billable duration)`,
|
|
32
|
+
`Upstream actual: ${money("actual")}`,
|
|
33
|
+
`Estimated: ${money("estimated")}`,
|
|
34
|
+
`History: ${result.history.complete ? "all retained facts" : "bounded partial facts"}`,
|
|
35
|
+
`Details: --limit 20 --offset ${result.details.nextOffset ?? 0}`,
|
|
36
|
+
...(limit === 0 ? [] : [JSON.stringify({ sessions: result.sessions, costs: result.costs }, null, 2)])
|
|
37
|
+
].join("\n") + "\n", result);
|
|
38
|
+
}
|
|
@@ -2,8 +2,9 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { snapshotSkillPackage, verifySkillPackage } from "./skillPackage.js";
|
|
5
6
|
import { SYSTEM_LEADER_ROLE, SYSTEM_OPERATOR_ROLE } from "../role/systemRoles.js";
|
|
6
|
-
const BUILTIN_YUI_SKILLS = new Set([
|
|
7
|
+
export const BUILTIN_YUI_SKILLS = new Set([
|
|
7
8
|
"yui-runtime",
|
|
8
9
|
"yui-operator",
|
|
9
10
|
"yui-leader",
|
|
@@ -22,7 +23,9 @@ export function compileRoleSessionContext(yuiHome, role, owner, options = {}) {
|
|
|
22
23
|
...(builtInSkillId === undefined ? [] : [builtInSkillId]),
|
|
23
24
|
...(role.skills ?? [])
|
|
24
25
|
]);
|
|
25
|
-
const skills =
|
|
26
|
+
const skills = options.skillPackages === undefined
|
|
27
|
+
? loadYuiSkillContexts(yuiHome, skillIds)
|
|
28
|
+
: options.skillPackages.map(loadSkillPackageContext);
|
|
26
29
|
return {
|
|
27
30
|
developerInstructions: renderDeveloperInstructions(kind, role, owner),
|
|
28
31
|
skills,
|
|
@@ -100,6 +103,14 @@ function loadSkill(yuiHome, id, builtIn) {
|
|
|
100
103
|
? resolve(fileURLToPath(new URL(`../../skills/${id}/`, import.meta.url)))
|
|
101
104
|
: resolve(join(yuiHome, "skills", id));
|
|
102
105
|
try {
|
|
106
|
+
if (yuiHome !== undefined) {
|
|
107
|
+
return loadSkillPackageContext(snapshotSkillPackage({
|
|
108
|
+
home: yuiHome,
|
|
109
|
+
root: builtIn ? resolve(path, "..") : path,
|
|
110
|
+
id, kind: builtIn ? "builtin" : "configured",
|
|
111
|
+
...(builtIn ? { subdirectory: id } : {})
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
103
114
|
return { id, path, content: readFileSync(join(path, "SKILL.md"), "utf8").trim() };
|
|
104
115
|
}
|
|
105
116
|
catch (error) {
|
|
@@ -109,6 +120,11 @@ function loadSkill(yuiHome, id, builtIn) {
|
|
|
109
120
|
throw error;
|
|
110
121
|
}
|
|
111
122
|
}
|
|
123
|
+
export function loadSkillPackageContext(ref) {
|
|
124
|
+
verifySkillPackage(ref);
|
|
125
|
+
return { id: ref.id, path: ref.path, content: readFileSync(join(ref.path, "SKILL.md"), "utf8").trim(),
|
|
126
|
+
package: ref };
|
|
127
|
+
}
|
|
112
128
|
function unique(values) {
|
|
113
129
|
return [...new Set(values)];
|
|
114
130
|
}
|