@zq-silk/yui 2.19.0 → 2.21.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 +5 -3
- package/dist/cli/invocationAuthority.js +2 -6
- package/dist/cli/managedDiagnostics.js +24 -16
- package/dist/commands/durableJobCommands.js +2 -3
- package/dist/commands/roleConfiguration.js +17 -0
- package/dist/commands/taskCommands.js +9 -36
- package/dist/commands/taskFactCommands.js +5 -5
- package/dist/commands/taskUsageCommand.js +2 -6
- package/dist/context/taskCatalog.js +4 -13
- package/dist/context/taskContext.js +35 -87
- package/dist/context/taskSearch.js +3 -7
- package/dist/controlPlaneCli.js +6 -18
- package/dist/controller/fileSchedulerStoreAdapter.js +22 -2
- package/dist/controller/jobClient.js +2 -2
- package/dist/controller/jobControl.js +2 -2
- package/dist/controller/jobDefinitionControl.js +5 -1
- package/dist/controller/runtimeLaunchCoordinator.js +12 -1
- package/dist/executor/agentExecutor.js +7 -0
- package/dist/executor/fileRoleLaunchPlanner.js +22 -6
- package/dist/executor/taskAgentCapabilities.js +2 -2
- package/dist/output/rolePresentation.js +1 -0
- package/dist/role/instanceCode.js +7 -0
- package/dist/role/role.js +10 -0
- package/dist/runtime/agentEndpoint.js +1 -0
- package/dist/runtime/agentHost.js +9 -1
- package/dist/runtime/launchBroker.js +3 -0
- package/dist/runtime/runtimeBinding.js +3 -0
- package/dist/runtime/sessionTitle.js +29 -9
- package/dist/runtime/structuredProviderHost.js +11 -1
- package/dist/runtime/tmuxAdapters.js +4 -0
- package/dist/storage/sqliteSchema.js +10 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/task/taskAuthority.js +7 -3
- package/docs/cli-information-contract.md +17 -0
- package/docs/cli-information-contract.zh-CN.md +12 -0
- package/docs/release-workflow.md +54 -0
- package/docs/release-workflow.zh-CN.md +40 -0
- package/docs/roles-and-configuration.md +49 -0
- package/docs/roles-and-configuration.zh-CN.md +38 -0
- package/docs/storage-baseline.md +4 -1
- package/docs/storage-baseline.zh-CN.md +1 -1
- package/docs/task-discovery.md +5 -5
- package/docs/task-discovery.zh-CN.md +4 -4
- package/docs/testing/verification-levels.md +2 -2
- package/docs/testing/verification-levels.zh-CN.md +1 -1
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +10 -6
- package/skills/yui-leader/VERSION +1 -1
- package/skills/yui-operator/SKILL.md +5 -3
- package/skills/yui-operator/VERSION +1 -1
- package/skills/yui-operator/references/task-delivery.md +4 -1
- package/skills/yui-reviewer/SKILL.md +4 -2
- package/skills/yui-reviewer/VERSION +1 -1
- package/skills/yui-runtime/SKILL.md +34 -8
- package/skills/yui-runtime/VERSION +1 -1
- package/skills/yui-runtime/references/publication.md +16 -9
- package/skills/yui-worker/SKILL.md +7 -4
- package/skills/yui-worker/VERSION +1 -1
|
@@ -136,6 +136,7 @@ const agentChildren = [
|
|
|
136
136
|
{ name: "remove", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Remove a configured Agent.", usage: "yui config agent remove <id>" }
|
|
137
137
|
];
|
|
138
138
|
const roleProfileOptions = [
|
|
139
|
+
"--instance-code",
|
|
139
140
|
"--description", "--responsibility", "--constraint",
|
|
140
141
|
"--expected-output", "--system-prompt", "--skill"
|
|
141
142
|
];
|
|
@@ -144,6 +145,7 @@ const roleAgentOptions = [
|
|
|
144
145
|
"--allowed-tool", "--disallowed-tool", "--search"
|
|
145
146
|
];
|
|
146
147
|
const roleProfileClearOptions = [
|
|
148
|
+
"--clear-instance-code",
|
|
147
149
|
"--clear-description", "--clear-responsibilities", "--clear-constraints",
|
|
148
150
|
"--clear-expected-output", "--clear-system-prompt", "--clear-skills"
|
|
149
151
|
];
|
|
@@ -399,19 +401,19 @@ const taskChildren = [
|
|
|
399
401
|
fileOptions: ["--summary-file"]
|
|
400
402
|
},
|
|
401
403
|
{
|
|
402
|
-
name: "list", discovery: { surface: "public", audiences: ["public", "operator", "leader"] },
|
|
404
|
+
name: "list", discovery: { surface: "public", audiences: ["public", "operator", "leader", "assignment"] },
|
|
403
405
|
summary: "Discover Tasks through a bounded, filterable catalog.",
|
|
404
406
|
usage: "yui task list [--all] [--source <ordinary|job>] [--status <status>] [--project <id>] [--search <text>] [--attention <category>] [--limit <1..100>] [--cursor <cursor>]",
|
|
405
407
|
options: ["--all", "--source", "--status", "--project", "--search", "--attention", "--limit", "--cursor"]
|
|
406
408
|
},
|
|
407
409
|
{
|
|
408
|
-
name: "search", discovery: { surface: "public", audiences: ["public", "operator", "leader"] },
|
|
410
|
+
name: "search", discovery: { surface: "public", audiences: ["public", "operator", "leader", "assignment"] },
|
|
409
411
|
summary: "Search authorized Task bodies and return exact, reference-only source citations.",
|
|
410
412
|
usage: "yui task search <query> [--task <id>] [--kind <brief|message|decision|result|completion>] [--limit <1..100>] [--cursor <cursor>]",
|
|
411
413
|
options: ["--task", "--kind", "--limit", "--cursor"]
|
|
412
414
|
},
|
|
413
415
|
{ name: "show", discovery: { surface: "public", audiences: ["public", "operator", "leader", "assignment"] }, summary: "Show Task metadata and counts; long details use contentPage.", usage: "yui task show <id> [--cursor <cursor>]", options: ["--cursor"] },
|
|
414
|
-
{ name: "usage", discovery: { surface: "public", audiences: ["public", "operator", "leader"] }, summary: "Read bounded Task usage and sourced costs; request detail pages explicitly.", usage: "yui task usage <task> [--limit <0..50>] [--offset <n>]", options: ["--limit", "--offset"] },
|
|
416
|
+
{ name: "usage", discovery: { surface: "public", audiences: ["public", "operator", "leader", "assignment"] }, summary: "Read bounded Task usage and sourced costs; request detail pages explicitly.", usage: "yui task usage <task> [--limit <0..50>] [--offset <n>]", options: ["--limit", "--offset"] },
|
|
415
417
|
{
|
|
416
418
|
name: "artifact", discovery: { surface: "managed", audiences: ["operator", "leader"] },
|
|
417
419
|
summary: "Save Task files in local Git and read current or commit-pinned content.",
|
|
@@ -4,7 +4,7 @@ import { requireManagedGlobalCaller } from "../runtime/managedCaller.js";
|
|
|
4
4
|
import { routeInvocation } from "./invocationRouter.js";
|
|
5
5
|
import { findInteractionPolicy } from "./interactionPolicy.js";
|
|
6
6
|
import { TASK_RECORD_ID_PREFIXES } from "../task/taskRecordReference.js";
|
|
7
|
-
import {
|
|
7
|
+
import { isTaskReadCommand } from "./managedDiagnostics.js";
|
|
8
8
|
/** Configuration reads remain available; every Home configuration mutation
|
|
9
9
|
* has one owner, irrespective of which config subcommand parses its flags. */
|
|
10
10
|
export function assertConfigurationAuthority(args, store, env) {
|
|
@@ -37,12 +37,8 @@ export function assertTaskInvocationScope(args, env) {
|
|
|
37
37
|
}
|
|
38
38
|
if (args[0] !== "task" || ["list", "search"].includes(args[1] ?? ""))
|
|
39
39
|
return;
|
|
40
|
-
|
|
41
|
-
if (diagnostic !== undefined) {
|
|
42
|
-
if (diagnostic !== env.YUI_TASK_ID)
|
|
43
|
-
throw usageError("Command target is outside the caller's Task.");
|
|
40
|
+
if (isTaskReadCommand(args))
|
|
44
41
|
return;
|
|
45
|
-
}
|
|
46
42
|
const invocation = routeInvocation([...args]);
|
|
47
43
|
if (invocation.kind !== "execute")
|
|
48
44
|
return;
|
|
@@ -1,20 +1,28 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
|
|
1
|
+
/** Explicit effect classification, not a verb-name heuristic. Ordinary reads
|
|
2
|
+
* authenticate the caller but do not borrow the target Task's authority.
|
|
3
|
+
* Mixed commands (retry, base status) stay mutating unless this exact form is
|
|
4
|
+
* read-only. Domain parsers still reject unknown flags and malformed targets. */
|
|
5
|
+
export function isTaskReadCommand(args) {
|
|
4
6
|
if (args[0] !== "task")
|
|
5
|
-
return
|
|
6
|
-
if (["show", "
|
|
7
|
-
return
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (
|
|
11
|
-
return args[3];
|
|
12
|
-
if (args[1] === "role" && args[2] === "session" && args[3] === "inspect")
|
|
13
|
-
return args[4];
|
|
14
|
-
if (["message", "run", "wake", "input"].includes(args[1] ?? "")
|
|
7
|
+
return false;
|
|
8
|
+
if (["list", "search", "show", "usage", "next-action", "remote-delivery", "archive-preflight"].includes(args[1] ?? ""))
|
|
9
|
+
return true;
|
|
10
|
+
if (args[1] === "context")
|
|
11
|
+
return true;
|
|
12
|
+
if (["brief", "decision", "milestone", "event", "message", "run", "wake", "input", "work", "publication"].includes(args[1] ?? "")
|
|
15
13
|
&& ["list", "show"].includes(args[2] ?? ""))
|
|
16
|
-
return
|
|
17
|
-
|
|
14
|
+
return true;
|
|
15
|
+
if (args[1] === "artifact" && ["list", "read"].includes(args[2] ?? ""))
|
|
16
|
+
return true;
|
|
17
|
+
if (args[1] === "run" && args[2] === "context")
|
|
18
|
+
return true;
|
|
19
|
+
if (args[1] === "role" && ["list", "status", "show"].includes(args[2] ?? ""))
|
|
20
|
+
return true;
|
|
21
|
+
if (args[1] === "role" && args[2] === "session") {
|
|
22
|
+
return args[3] === "inspect"
|
|
23
|
+
|| args[3] === "retry" && (args.length === 6 || args.length === 7 && args[6] === "show");
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
18
26
|
}
|
|
19
27
|
export function operatorOfflineCommand(args) {
|
|
20
28
|
return (args[0] === "controller"
|
|
@@ -25,5 +33,5 @@ export function operatorOfflineCommand(args) {
|
|
|
25
33
|
|| (args[0] === "session" && ["context", "reconcile"].includes(args[1] ?? ""))
|
|
26
34
|
|| (args[0] === "role" && args[1] === "message" && ["list", "show"].includes(args[2] ?? ""))
|
|
27
35
|
|| (args[0] === "task" && args[1] === "list")
|
|
28
|
-
||
|
|
36
|
+
|| isTaskReadCommand(args);
|
|
29
37
|
}
|
|
@@ -2,7 +2,7 @@ import { usageError } from "../errors/cliError.js";
|
|
|
2
2
|
import { ensureFileTaskController } from "../controller/clientRuntime.js";
|
|
3
3
|
import { callFileTaskController } from "../controller/clientRuntime.js";
|
|
4
4
|
import { acknowledgeDurableJob, cancelDurableJob, getDurableJob, startDurableJob } from "../controller/jobClient.js";
|
|
5
|
-
import { resolveJobCaller, taskLocalActor } from "../task/taskAuthority.js";
|
|
5
|
+
import { resolveJobCaller, resolveJobReadCaller, taskLocalActor } from "../task/taskAuthority.js";
|
|
6
6
|
import { boundedJobArtifactPayload } from "../artifacts/jobArtifactTransport.js";
|
|
7
7
|
import { readFileSync, statSync } from "node:fs";
|
|
8
8
|
/**
|
|
@@ -177,8 +177,7 @@ async function startJob(args, options) {
|
|
|
177
177
|
}
|
|
178
178
|
async function getJob(args, options) {
|
|
179
179
|
const ref = parseRefArgs(args, "get");
|
|
180
|
-
const caller =
|
|
181
|
-
await ensureFileTaskController(options.home, { environment: options.environment });
|
|
180
|
+
const caller = resolveJobReadCaller(options.environment, ref.taskId);
|
|
182
181
|
const job = await getDurableJob(options.home, ref.taskId, ref.jobId, caller);
|
|
183
182
|
if (options.json === true)
|
|
184
183
|
return `${JSON.stringify(job, null, 2)}\n`;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { usageError } from "../errors/cliError.js";
|
|
2
|
+
import { validateInstanceCode } from "../role/instanceCode.js";
|
|
2
3
|
import { parseEnabled } from "../role/availability.js";
|
|
3
4
|
import { defaultRoleAgentConfig, resolveAgentAdapter } from "../executor/agentAdapter.js";
|
|
4
5
|
const PROFILE_OPTIONS = [
|
|
@@ -66,6 +67,8 @@ export function roleOptionSpecs(input) {
|
|
|
66
67
|
...(input.includeWorkspace === true ? [["--workspace", "value"]] : []),
|
|
67
68
|
...(input.update ? [["--yes", "flag"]] : []),
|
|
68
69
|
...(input.update ? [["--enabled", "value"]] : []),
|
|
70
|
+
["--instance-code", "value"],
|
|
71
|
+
...(input.update ? [["--clear-instance-code", "flag"]] : []),
|
|
69
72
|
...PROFILE_OPTIONS,
|
|
70
73
|
...agentValueOptions,
|
|
71
74
|
...agentRepeatableOptions,
|
|
@@ -105,6 +108,7 @@ export function parseRoleOptions(args, specs, usage) {
|
|
|
105
108
|
}
|
|
106
109
|
export function roleProfileFrom(parsed) {
|
|
107
110
|
return {
|
|
111
|
+
...roleDisplayPatch(parsed),
|
|
108
112
|
...(parsed.has("--description") ? { description: requiredText(parsed.one("--description")) } : {}),
|
|
109
113
|
responsibilities: parsed.many("--responsibility").map(requiredText),
|
|
110
114
|
constraints: parsed.many("--constraint").map(requiredText),
|
|
@@ -125,6 +129,7 @@ export function roleProfilePatch(parsed) {
|
|
|
125
129
|
["--skill", "--clear-skills"]
|
|
126
130
|
]);
|
|
127
131
|
return {
|
|
132
|
+
...roleDisplayPatch(parsed),
|
|
128
133
|
...(parsed.has("--enabled") ? { enabled: parseEnabled(parsed.one("--enabled")) } : {}),
|
|
129
134
|
...(parsed.has("--description") ? { description: requiredText(parsed.one("--description")) } : {}),
|
|
130
135
|
...(parsed.has("--clear-description") ? { description: undefined } : {}),
|
|
@@ -144,6 +149,18 @@ export function roleProfilePatch(parsed) {
|
|
|
144
149
|
...(parsed.has("--clear-skills") ? { skills: [] } : {})
|
|
145
150
|
};
|
|
146
151
|
}
|
|
152
|
+
function roleDisplayPatch(parsed) {
|
|
153
|
+
assertPairs(parsed, [["--instance-code", "--clear-instance-code"]]);
|
|
154
|
+
try {
|
|
155
|
+
return {
|
|
156
|
+
...(parsed.has("--instance-code") ? { instanceCode: validateInstanceCode(parsed.one("--instance-code")) } : {}),
|
|
157
|
+
...(parsed.has("--clear-instance-code") ? { instanceCode: undefined } : {})
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
throw usageError(error instanceof Error ? error.message : String(error));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
147
164
|
export function hasAgentConfigOptions(parsed) {
|
|
148
165
|
return [...parsed.seen].some((option) => AGENT_CONFIG_OPTIONS.has(option));
|
|
149
166
|
}
|
|
@@ -11,7 +11,7 @@ import { sourceRunContextValue } from "../context/sourceRunContext.js";
|
|
|
11
11
|
import { buildRunContextDelta, buildRunContextPack, runContextEntry, runContextPointerPage, contextSnapshotDeltaRefIds, expandRunContextRef, freezeReviewStageContextSnapshot, freezeRunContextSnapshot, freezeWorkItemExecutionAssignmentContextSnapshot, freezeCandidateContextSnapshot, readRunContextSnapshot, synthesisSourceRunIds } from "../context/runContextPack.js";
|
|
12
12
|
import { createRunInput } from "../context/runInputContract.js";
|
|
13
13
|
import { parseTaskCatalogOptions, readTaskCatalog, renderTaskCatalog } from "../context/taskCatalog.js";
|
|
14
|
-
import {
|
|
14
|
+
import { assertTaskReadable, readTaskContextResource, resolveContextReader } from "../context/taskContext.js";
|
|
15
15
|
import { parseTaskSearchOptions, searchTaskBodies } from "../context/taskSearch.js";
|
|
16
16
|
import { boundedDocument, readOptions, messageReceipt, recordPage } from "../output/boundedRead.js";
|
|
17
17
|
import { referencedWakeRunIds } from "../context/wakeRunReferences.js";
|
|
@@ -46,7 +46,7 @@ import { resolveRecordedTaskFinalReviewContract } from "../review/taskFinalRevie
|
|
|
46
46
|
import { copyGlobalRoleToTaskRole, activeRoleAgentBinding, createRole, createRoleAgentBinding, switchActiveRoleAgent, unbindRoleAgent, updateRole } from "../role/role.js";
|
|
47
47
|
import { roleLaunchEventPayload, saveTaskRoleUpdate } from "../role/taskRoleUpdate.js";
|
|
48
48
|
import { RUNTIME_SESSION_REPLACE_REQUIRED_REASON, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
|
|
49
|
-
import { currentManagedRuntime
|
|
49
|
+
import { currentManagedRuntime } from "../runtime/managedCaller.js";
|
|
50
50
|
import { controlProviderRetry, providerRunRetry, providerRetryProjection, renderProviderRetry } from "../runtime/providerRetry.js";
|
|
51
51
|
import { currentProviderConversation, transferProviderAuthority } from "../runtime/providerRuntimeIdentity.js";
|
|
52
52
|
import { projectProviderContinuations } from "../runtime/runtimeContinuationProjection.js";
|
|
@@ -1684,7 +1684,7 @@ function taskMessageCommand(args, store, options) {
|
|
|
1684
1684
|
const parsed = readOptions(rest, ["--cursor"]);
|
|
1685
1685
|
exactPositionals(parsed.positionals, 1, usage);
|
|
1686
1686
|
const ref = taskRecordReference(parsed.positionals[0], "message", "Message reference", options);
|
|
1687
|
-
|
|
1687
|
+
assertTaskReadable(store, ref.taskId, options.environment);
|
|
1688
1688
|
const message = store.queryContextRecords(ref.taskId, {
|
|
1689
1689
|
family: "task-message", ids: [ref.localId], limit: 1
|
|
1690
1690
|
}).records[0]?.value;
|
|
@@ -2512,14 +2512,8 @@ function taskRoleSessionCommand(args, store, options) {
|
|
|
2512
2512
|
const role = requireRole(tx, task.id, roleName);
|
|
2513
2513
|
if (action !== "show")
|
|
2514
2514
|
taskActor(tx, options, task.id);
|
|
2515
|
-
else
|
|
2516
|
-
|
|
2517
|
-
if (reader === undefined)
|
|
2518
|
-
taskActor(tx, options, task.id);
|
|
2519
|
-
else if (reader.taskId !== task.id || reader.roleName !== "leader" && reader.roleName !== role.name) {
|
|
2520
|
-
throw usageError("Provider retry read is outside the caller's Task/Role.");
|
|
2521
|
-
}
|
|
2522
|
-
}
|
|
2515
|
+
else
|
|
2516
|
+
resolveContextReader(tx, options.environment ?? {});
|
|
2523
2517
|
const sessions = tx.getTaskRoleSessionSet(task.id, role.name);
|
|
2524
2518
|
let binding = sessions?.providerBinding ?? null;
|
|
2525
2519
|
if (action !== "show") {
|
|
@@ -2538,12 +2532,7 @@ function taskRoleSessionCommand(args, store, options) {
|
|
|
2538
2532
|
exactPositionals(rest, 2, "Task Role Session inspect usage: yui task role session inspect <task> <role>.");
|
|
2539
2533
|
const task = requireTask(store, rest[0]);
|
|
2540
2534
|
const role = requireRole(store, task.id, rest[1]);
|
|
2541
|
-
|
|
2542
|
-
if (reader === undefined)
|
|
2543
|
-
taskActor(store, options, task.id);
|
|
2544
|
-
else if (reader.taskId !== task.id || (reader.roleName !== "leader" && reader.roleName !== role.name)) {
|
|
2545
|
-
throw usageError("Session inspection is outside the caller's Task/Role.");
|
|
2546
|
-
}
|
|
2535
|
+
resolveContextReader(store, options.environment ?? {});
|
|
2547
2536
|
const sessions = store.getTaskRoleSessionSet(task.id, role.name);
|
|
2548
2537
|
const active = sessions?.sessions[sessions.activeAgentId] ?? null;
|
|
2549
2538
|
const binding = sessions?.providerBinding ?? null;
|
|
@@ -4823,30 +4812,14 @@ function parseRunContextReference(value) {
|
|
|
4823
4812
|
}
|
|
4824
4813
|
return { taskId, runId };
|
|
4825
4814
|
}
|
|
4826
|
-
/**
|
|
4827
|
-
*
|
|
4828
|
-
* work. Authorization is therefore scope-shaped, not schedule-shaped: the
|
|
4829
|
-
* caller must be the current runtime of the Task Role that owns the AgentRun,
|
|
4830
|
-
* proven by its per-Session caller key against durable state.
|
|
4831
|
-
*
|
|
4832
|
-
* It deliberately does not require the AgentRun to still be the active one. An
|
|
4833
|
-
* Agent whose AgentRun has advanced must still be able to read the context it was
|
|
4834
|
-
* given; losing read access to its own Role's history is what forces an
|
|
4835
|
-
* otherwise healthy Agent to stop and escalate to a human.
|
|
4836
|
-
*/
|
|
4815
|
+
/** Frozen packs are readable evidence. Their execution authority still belongs
|
|
4816
|
+
* exclusively to the dispatched Run; reading does not adopt that identity. */
|
|
4837
4817
|
function authorizeRunContext(store, taskId, runId, environment) {
|
|
4838
|
-
|
|
4839
|
-
|| environment?.YUI_TASK_ID !== undefined
|
|
4840
|
-
|| environment?.YUI_ROLE !== undefined;
|
|
4841
|
-
if (!managed)
|
|
4842
|
-
return;
|
|
4818
|
+
resolveContextReader(store, environment ?? {});
|
|
4843
4819
|
const run = store.getRun(taskId, runId);
|
|
4844
4820
|
if (run === null) {
|
|
4845
4821
|
throw usageError(`AgentRun Context access is not authorized: ${taskId}/${runId}.`);
|
|
4846
4822
|
}
|
|
4847
|
-
if (currentManagedRuntime(store, environment, taskId, run.roleName) === undefined) {
|
|
4848
|
-
throw usageError(`AgentRun Context access requires the current runtime of ${taskId}/${run.roleName}.`);
|
|
4849
|
-
}
|
|
4850
4823
|
}
|
|
4851
4824
|
function listRuns(args, store, options) {
|
|
4852
4825
|
const reference = args[0];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createTaskBrief, updateTaskBrief } from "../brief/taskBrief.js";
|
|
2
|
-
import {
|
|
2
|
+
import { assertTaskReadable, resolveContextReader } from "../context/taskContext.js";
|
|
3
3
|
import { taskRecordList } from "./taskContextCommand.js";
|
|
4
4
|
import { materialize } from "../context/taskContext.js";
|
|
5
5
|
import { enqueueWork } from "../coordination/workMailboxQueue.js";
|
|
@@ -15,7 +15,7 @@ export function taskBriefCommand(args, store, options) {
|
|
|
15
15
|
exactPositionals(rest, 1, "Task brief show usage: yui task brief show <task>.");
|
|
16
16
|
const { task, brief, writeGuard, phase } = store.readTransaction(reader => {
|
|
17
17
|
const task = requireTask(reader, rest[0]);
|
|
18
|
-
|
|
18
|
+
assertTaskReadable(reader, task.id, options.environment);
|
|
19
19
|
const caller = resolveContextReader(reader, options.environment ?? {});
|
|
20
20
|
const shared = caller === undefined || caller.roleName === "leader";
|
|
21
21
|
return { task, brief: reader.getTaskBrief(task.id),
|
|
@@ -144,7 +144,7 @@ export function taskDecisionCommand(args, store, options) {
|
|
|
144
144
|
if (command === "show") {
|
|
145
145
|
exactPositionals(rest, 2, "Task decision show usage: yui task decision show <task> <decision>.");
|
|
146
146
|
const task = requireTask(store, rest[0]);
|
|
147
|
-
|
|
147
|
+
assertTaskReadable(store, task.id, options.environment);
|
|
148
148
|
const decision = store.getDecision(task.id, rest[1]);
|
|
149
149
|
if (decision === null)
|
|
150
150
|
throw dataError(`Decision not found: ${rest[1]}.`);
|
|
@@ -228,7 +228,7 @@ export function taskMilestoneCommand(args, store, options) {
|
|
|
228
228
|
if (command === "show") {
|
|
229
229
|
exactPositionals(rest, 2, "Task milestone show usage: yui task milestone show <task> <milestone>.");
|
|
230
230
|
const task = requireTask(store, rest[0]);
|
|
231
|
-
|
|
231
|
+
assertTaskReadable(store, task.id, options.environment);
|
|
232
232
|
const milestone = store.getMilestone(task.id, rest[1]);
|
|
233
233
|
if (milestone === null)
|
|
234
234
|
throw dataError(`Milestone not found: ${rest[1]}.`);
|
|
@@ -254,7 +254,7 @@ export function taskEventCommand(args, store, options = {}) {
|
|
|
254
254
|
if (command === "show") {
|
|
255
255
|
exactPositionals(rest, 2, "Task event show usage: yui task event show <task> <event>.");
|
|
256
256
|
const task = requireTask(store, rest[0]);
|
|
257
|
-
|
|
257
|
+
assertTaskReadable(store, task.id, options.environment);
|
|
258
258
|
const event = store.queryContextRecords(task.id, {
|
|
259
259
|
family: "task-event", ids: [rest[1]], limit: 1
|
|
260
260
|
}).records[0]?.value;
|
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assertTaskReadable } from "../context/taskContext.js";
|
|
2
2
|
import { readTaskUsage } from "../runtime/taskUsageQuery.js";
|
|
3
3
|
import { formatUsageMetric } from "../runtime/taskUsageMetrics.js";
|
|
4
4
|
import { exactPositionals, output, parseTail, requireTask } from "./taskCommandSupport.js";
|
|
5
5
|
import { usageError } from "../errors/cliError.js";
|
|
6
|
-
import { taskActor } from "../task/taskAuthority.js";
|
|
7
6
|
export function taskUsageCommand(args, store, options) {
|
|
8
7
|
const usage = "yui task usage <task> [--limit <0..50>] [--offset <n>]";
|
|
9
8
|
const parsed = parseTail(args, new Set(["--limit", "--offset"]), usage);
|
|
10
9
|
exactPositionals(parsed.positionals, 1, usage);
|
|
11
10
|
const task = requireTask(store, parsed.positionals[0]);
|
|
12
|
-
|
|
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);
|
|
11
|
+
assertTaskReadable(store, task.id, options.environment);
|
|
16
12
|
const limit = Number(parsed.options.get("--limit") ?? 0), offset = Number(parsed.options.get("--offset") ?? 0);
|
|
17
13
|
if (!Number.isSafeInteger(limit) || limit < 0 || limit > 50 || !Number.isSafeInteger(offset) || offset < 0) {
|
|
18
14
|
throw usageError(usage);
|
|
@@ -50,26 +50,17 @@ export function parseTaskCatalogOptions(args) {
|
|
|
50
50
|
...(values.has("--cursor") ? { cursor: values.get("--cursor") } : {})
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
-
/** Task-local Leaders discover only their Task. Assignment Roles use their
|
|
54
|
-
* existing Context pack: whole-Task counts would leak unrelated assignments. */
|
|
55
|
-
export function taskCatalogScope(store, environment) {
|
|
56
|
-
const caller = resolveContextReader(store, environment);
|
|
57
|
-
if (caller !== undefined && caller.roleName !== "leader") {
|
|
58
|
-
throw usageError("Task discovery is unavailable to an Assignment-scoped Role; use its authorized Run Context.");
|
|
59
|
-
}
|
|
60
|
-
return caller?.taskId;
|
|
61
|
-
}
|
|
62
53
|
export function readTaskCatalog(store, options, environment = {}) {
|
|
63
54
|
return store.transaction(reader => {
|
|
64
|
-
|
|
55
|
+
resolveContextReader(reader, environment);
|
|
65
56
|
const { cursor: rawCursor, ...filters } = options;
|
|
66
57
|
if (!Number.isSafeInteger(filters.limit) || filters.limit < 1 || filters.limit > MAX_LIMIT) {
|
|
67
58
|
throw usageError("Task catalog limit must be between 1 and 100.");
|
|
68
59
|
}
|
|
69
|
-
const scope = contextContentDigest({ taskId:
|
|
60
|
+
const scope = contextContentDigest({ taskId: null, ...filters });
|
|
70
61
|
const cursor = rawCursor === undefined ? undefined : decodeCursor(rawCursor, scope);
|
|
71
62
|
const facts = reader.queryTaskCatalog({
|
|
72
|
-
...filters,
|
|
63
|
+
...filters,
|
|
73
64
|
...(cursor === undefined ? {} : { after: cursor.after, through: cursor.through }),
|
|
74
65
|
limit: options.limit + 1
|
|
75
66
|
});
|
|
@@ -106,7 +97,7 @@ export function readTaskCatalog(store, options, environment = {}) {
|
|
|
106
97
|
}));
|
|
107
98
|
const response = {
|
|
108
99
|
view: "compact",
|
|
109
|
-
scope: { taskId:
|
|
100
|
+
scope: { taskId: null, archived: options.all ? "included" : "excluded",
|
|
110
101
|
attention: "authorized-catalog-before-filters", executionSignals: "raw-inspection-candidates-not-execution-status" },
|
|
111
102
|
filters, total: facts.total, counts: facts.counts, attention,
|
|
112
103
|
tasks: [],
|