@zq-silk/yui 2.20.0 → 2.22.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 +5 -0
- package/dist/cli/commandCatalog.js +88 -28
- package/dist/cli/dynamicCompletion.js +4 -1
- package/dist/cli/helpRenderer.js +12 -1
- package/dist/cli/interactionPolicy.js +2 -0
- package/dist/cli/invocationAuthority.js +2 -6
- package/dist/cli/invocationRouter.js +5 -5
- package/dist/cli/managedDiagnostics.js +24 -16
- package/dist/cli/roleWizard.js +5 -1
- package/dist/commands/durableJobCommands.js +2 -3
- package/dist/commands/globalRoleCommands.js +33 -8
- package/dist/commands/inboxCommands.js +39 -12
- package/dist/commands/operatorCommands.js +2 -5
- package/dist/commands/releaseCommands.js +16 -2
- package/dist/commands/roleConfiguration.js +25 -2
- package/dist/commands/sessionCommands.js +5 -2
- package/dist/commands/taskCommands.js +225 -99
- package/dist/commands/taskContextCommand.js +26 -4
- package/dist/commands/taskFactCommands.js +20 -7
- package/dist/commands/taskIntegrationCommands.js +3 -3
- package/dist/commands/taskRoleRuntimeStatus.js +15 -5
- package/dist/commands/taskUsageCommand.js +2 -6
- package/dist/commands/textInput.js +2 -2
- package/dist/context/contextFamilies.js +10 -0
- package/dist/context/currentSkills.js +6 -4
- package/dist/context/runContextPack.js +44 -5
- package/dist/context/runInputContract.js +1 -1
- package/dist/context/sessionBootstrapManifest.js +7 -5
- package/dist/context/sessionSkillNotice.js +53 -0
- package/dist/context/taskCatalog.js +4 -13
- package/dist/context/taskContext.js +135 -103
- package/dist/context/taskSearch.js +3 -7
- package/dist/controlPlaneCli.js +23 -25
- package/dist/controller/agentRuntimeObserver.js +57 -12
- package/dist/controller/clientRuntime.js +37 -8
- package/dist/controller/controller.js +3 -1
- package/dist/controller/diagnosticJournal.js +10 -2
- package/dist/controller/fileSchedulerStoreAdapter.js +72 -24
- package/dist/controller/jobClient.js +2 -2
- package/dist/controller/jobControl.js +2 -2
- package/dist/controller/jobDefinitionControl.js +5 -1
- package/dist/controller/messageHealth.js +4 -3
- package/dist/controller/providerRetryAdmission.js +15 -1
- package/dist/controller/providerRetryDelivery.js +18 -2
- package/dist/controller/runtime.js +15 -5
- package/dist/controller/runtimeEventInbox.js +107 -13
- package/dist/controller/runtimeEventProcessor.js +3 -3
- package/dist/controller/taskRunRecovery.js +153 -0
- package/dist/core/controllerClient.js +16 -2
- package/dist/core/controllerServer.js +14 -5
- package/dist/core/controllerTelemetry.js +51 -5
- package/dist/errors/cliFailure.js +14 -5
- package/dist/executor/agentAdapter.js +32 -4
- package/dist/executor/agentConfigurationFields.js +2 -0
- package/dist/executor/taskAgentCapabilities.js +2 -2
- package/dist/integration/gitIntegrationService.js +3 -2
- package/dist/integration/integrationSourceApplication.js +12 -1
- package/dist/interaction/notificationPresentation.js +11 -16
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/message/messageContinuation.js +3 -1
- package/dist/output/boundedRead.js +36 -7
- package/dist/output/rolePresentation.js +3 -1
- package/dist/runtime/agentHost.js +12 -3
- package/dist/runtime/codexAppServerRuntime.js +110 -24
- package/dist/runtime/codexInteractiveHost.js +46 -0
- package/dist/runtime/providerRetry.js +5 -2
- package/dist/runtime/providerRuntimeIdentity.js +1 -1
- package/dist/runtime/structuredProviderHost.js +32 -34
- package/dist/scheduler/wakeupQueue.js +10 -4
- package/dist/storage/sqliteSchema.js +9 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/task/taskAuthority.js +7 -3
- package/dist/tmux/tmuxManager.js +4 -2
- package/dist/web/webSettings.js +2 -0
- package/docs/authorization.md +18 -3
- package/docs/cli-information-contract.md +245 -6
- package/docs/cli-information-contract.zh-CN.md +107 -5
- package/docs/managed-turn-and-session-runtime.md +82 -21
- package/docs/managed-turn-and-session-runtime.zh-CN.md +56 -13
- package/docs/provider-retry.md +11 -0
- package/docs/release-workflow.md +62 -0
- package/docs/release-workflow.zh-CN.md +46 -0
- package/docs/roles-and-configuration.md +28 -0
- package/docs/roles-and-configuration.zh-CN.md +21 -0
- package/docs/storage-baseline.md +4 -1
- package/docs/storage-baseline.zh-CN.md +1 -1
- package/docs/task-delivery.md +7 -0
- package/docs/task-delivery.zh-CN.md +5 -0
- package/docs/task-discovery.md +5 -5
- package/docs/task-discovery.zh-CN.md +4 -4
- package/docs/testing/review-evaluation.md +7 -7
- package/docs/testing/review-evaluation.zh-CN.md +7 -7
- package/docs/testing/verification-levels.md +45 -2
- package/docs/testing/verification-levels.zh-CN.md +21 -2
- package/i18n/README.zh-CN.md +6 -0
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +98 -189
- package/skills/yui-leader/VERSION +1 -1
- package/skills/yui-leader/references/artifacts.md +41 -0
- package/skills/yui-leader/references/assignment-execution.md +140 -0
- package/skills/yui-leader/references/completion.md +79 -0
- package/skills/yui-leader/references/delivery-scope.md +26 -0
- package/skills/yui-leader/references/execution.md +16 -487
- package/skills/yui-leader/references/implementation.md +81 -0
- package/skills/yui-leader/references/planning.md +7 -45
- package/skills/yui-leader/references/review.md +90 -0
- package/skills/yui-leader/references/task-progress.md +23 -0
- package/skills/yui-leader/references/work-allocation.md +118 -0
- package/skills/yui-operator/SKILL.md +77 -331
- package/skills/yui-operator/VERSION +1 -1
- package/skills/yui-operator/references/body-transport.md +62 -0
- package/skills/yui-operator/references/configuration.md +47 -0
- package/skills/yui-operator/references/lifecycle.md +41 -0
- package/skills/yui-operator/references/progress.md +39 -0
- package/skills/yui-operator/references/routing.md +57 -0
- package/skills/yui-operator/references/submission.md +56 -0
- package/skills/yui-operator/references/task-delivery.md +5 -2
- package/skills/yui-operator/references/web-lifecycle.md +19 -0
- package/skills/yui-operator/scripts/verify-submission.mjs +37 -0
- package/skills/yui-reviewer/SKILL.md +4 -2
- package/skills/yui-reviewer/VERSION +1 -1
- package/skills/yui-runtime/SKILL.md +177 -505
- package/skills/yui-runtime/VERSION +1 -1
- package/skills/yui-runtime/references/archive.md +51 -0
- package/skills/yui-runtime/references/command-discovery.md +41 -0
- package/skills/yui-runtime/references/completed-followup.md +65 -0
- package/skills/yui-runtime/references/context-ordering.md +3 -34
- package/skills/yui-runtime/references/context-reads.md +61 -0
- package/skills/yui-runtime/references/dispatched-run.md +61 -0
- package/skills/yui-runtime/references/execution-failure.md +105 -0
- package/skills/yui-runtime/references/global-context.md +18 -0
- package/skills/yui-runtime/references/global-input-controls.md +44 -0
- package/skills/yui-runtime/references/host-custody.md +30 -0
- package/skills/yui-runtime/references/inbox-recovery.md +77 -0
- package/skills/yui-runtime/references/incremental-context.md +40 -0
- package/skills/yui-runtime/references/publication.md +4 -104
- package/skills/yui-runtime/references/recovery.md +32 -192
- package/skills/yui-runtime/references/session-identity.md +18 -0
- package/skills/yui-runtime/references/session-replacement.md +45 -0
- package/skills/yui-runtime/references/uncertain-execution.md +29 -0
- package/skills/yui-worker/SKILL.md +7 -4
- package/skills/yui-worker/VERSION +1 -1
|
@@ -2,6 +2,7 @@ import { runExecutionObservation } from "../agentRun/agentRun.js";
|
|
|
2
2
|
import { isGitArtifactRefString, parseGitArtifactRef } from "../artifacts/gitArtifactRef.js";
|
|
3
3
|
import { taskNotFound, usageError } from "../errors/cliError.js";
|
|
4
4
|
import { expandTaskMessageResult } from "../message/message.js";
|
|
5
|
+
import { taskMessagePresentation } from "../interaction/notificationPresentation.js";
|
|
5
6
|
import { currentManagedRuntime, resolveManagedTaskReader } from "../runtime/managedCaller.js";
|
|
6
7
|
import { hasManagedIdentity } from "../runtime/managedIdentity.js";
|
|
7
8
|
import { providerRetrySummary } from "../runtime/providerRetry.js";
|
|
@@ -9,15 +10,22 @@ import { projectTaskRemoteDeliveryFromStore } from "../task/remoteDeliveryServic
|
|
|
9
10
|
import { retiredTaskRecordIds, TASK_RECORD_RETIRED_EVENT } from "../task/taskRecordRetirement.js";
|
|
10
11
|
import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
|
|
11
12
|
import { contextContentDigest } from "./contextSnapshot.js";
|
|
12
|
-
import { buildRunContextPack } from "./runContextPack.js";
|
|
13
13
|
import { sourceRunContextValue } from "./sourceRunContext.js";
|
|
14
|
-
import { boundedDocument, fitsReadBudget, summaryText, recordPage } from "../output/boundedRead.js";
|
|
14
|
+
import { boundedDocument, fitsReadBudget, summaryText, recordPage, readDigest } from "../output/boundedRead.js";
|
|
15
15
|
import { taskBriefGuard, taskPhase } from "./taskPhase.js";
|
|
16
|
+
import { CONTEXT_FAMILIES } from "./contextFamilies.js";
|
|
16
17
|
const MAX_RECORDS = 64;
|
|
17
18
|
const MAX_VALUE_BYTES = 2048;
|
|
18
19
|
const MAX_PAGE_BYTES = 24 * 1024;
|
|
19
20
|
const MAX_EVENTS = 100;
|
|
20
21
|
const MAX_ATTENTION_REFS = 8;
|
|
22
|
+
// The current read is a working index, not a replay of configuration, workspace
|
|
23
|
+
// inventories and requirements on every wake. Exact bodies use the same scoped,
|
|
24
|
+
// digest-checked inspect path regardless of how small a record happens to be.
|
|
25
|
+
const INDEX_ONLY_FAMILIES = new Set([
|
|
26
|
+
"task", "task-brief", "remote-delivery", "role", "project-policy",
|
|
27
|
+
"managed-workspace", "task-decision", "task-message", "run", "work-item", "project-knowledge"
|
|
28
|
+
]);
|
|
21
29
|
/** A current read model, not a second snapshot store or a delivery receipt.
|
|
22
30
|
* Every public path checks the same scope before reading, counting or paging.
|
|
23
31
|
*/
|
|
@@ -27,12 +35,12 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
27
35
|
const sources = authorizedSources(reader, scope, true);
|
|
28
36
|
const count = sources.reduce((total, source) => total + source.count, 0);
|
|
29
37
|
const cursor = currentCursor(reader, taskId);
|
|
30
|
-
const phase =
|
|
38
|
+
const phase = taskPhase(reader, scope.task);
|
|
31
39
|
const phaseSource = phase?.activation?.source;
|
|
32
40
|
const priorMessageId = phaseSource?.priorMessageId;
|
|
33
41
|
// Reserve a small, classified view before ordinary history consumes the
|
|
34
42
|
// page. Counts and samples come from the exact same authorized read.
|
|
35
|
-
const attention = summarizeAttention(reader, taskId
|
|
43
|
+
const attention = summarizeAttention(reader, taskId);
|
|
36
44
|
let bytes = Buffer.byteLength(JSON.stringify(attention));
|
|
37
45
|
const records = [];
|
|
38
46
|
let runtimeEvents;
|
|
@@ -45,8 +53,8 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
45
53
|
if (entry.ref.store === "run")
|
|
46
54
|
runtimeEvents ??= reader.listEventsByType(taskId, ["runtime.agent-error"]);
|
|
47
55
|
const valueBytes = Buffer.byteLength(JSON.stringify(entry.value));
|
|
48
|
-
const content = valueBytes > MAX_VALUE_BYTES ||
|
|
49
|
-
? { ref: entry.ref, summary:
|
|
56
|
+
const content = valueBytes > MAX_VALUE_BYTES || INDEX_ONLY_FAMILIES.has(entry.ref.store)
|
|
57
|
+
? { ref: entry.ref, summary: summarizeEntry(reader, entry), omitted: true }
|
|
50
58
|
: { ...entry, omitted: false };
|
|
51
59
|
const revisedAfterActivation = entry.ref.store === "task-message" && phaseSource?.ref !== undefined
|
|
52
60
|
&& reader.queryContextRecords(taskId, {
|
|
@@ -62,9 +70,9 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
62
70
|
? "recorded-before-activation-request" : "recorded-after-activation-request"
|
|
63
71
|
} : {}),
|
|
64
72
|
...(entry.ref.store === "run" ? {
|
|
65
|
-
execution:
|
|
73
|
+
execution: currentRunExecution(reader, entry.value, runtimeEvents)
|
|
66
74
|
} : {}), ...(entry.ref.store === "role" ? {
|
|
67
|
-
providerRetry: providerRetrySummary(reader.getTaskRoleSessionSet(taskId, entry.value.name)?.providerBinding)
|
|
75
|
+
providerRetry: providerRetrySummary(reader.getTaskRoleSessionSet(taskId, entry.value.name)?.providerBinding, `yui task role session retry ${taskId} ${entry.value.name} show --json`)
|
|
68
76
|
} : {}) };
|
|
69
77
|
const size = Buffer.byteLength(JSON.stringify(record));
|
|
70
78
|
if (bytes + size > MAX_PAGE_BYTES)
|
|
@@ -75,13 +83,13 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
75
83
|
const result = {
|
|
76
84
|
taskId, coreCursor: encode(cursor), throughCursor: encode(cursor),
|
|
77
85
|
view: "current",
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
briefWriteGuard: taskBriefGuard(reader, scope.task)
|
|
81
|
-
} : {}),
|
|
86
|
+
phase,
|
|
87
|
+
briefWriteGuard: taskBriefGuard(reader, scope.task),
|
|
82
88
|
caller: scope.caller === undefined ? { kind: "unmanaged-or-global" } : {
|
|
89
|
+
taskId: scope.caller.taskId,
|
|
83
90
|
role: scope.caller.roleName,
|
|
84
|
-
executionAuthority:
|
|
91
|
+
executionAuthority: scope.caller.taskId !== taskId ? "reference-only"
|
|
92
|
+
: currentManagedRuntime(reader, environment, taskId)?.executionAuthority ?? "historical-read-only"
|
|
85
93
|
},
|
|
86
94
|
collections: Object.entries(sources.reduce((counts, source) => {
|
|
87
95
|
counts[source.family] = (counts[source.family] ?? 0) + source.count;
|
|
@@ -118,7 +126,16 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
118
126
|
return result;
|
|
119
127
|
});
|
|
120
128
|
}
|
|
121
|
-
|
|
129
|
+
/** Keep live delivery/attempt facts, but do not replay retry diagnostics and
|
|
130
|
+
* history into the working index. Exact Run inspect below retains the full
|
|
131
|
+
* observation, under the same authorization and document-pagination contract.
|
|
132
|
+
*/
|
|
133
|
+
function currentRunExecution(store, run, events) {
|
|
134
|
+
const binding = store.getTaskRoleSessionSet(run.taskId, run.roleName)?.providerBinding;
|
|
135
|
+
const execution = runExecutionObservation(run, binding, events);
|
|
136
|
+
return { ...execution, retry: execution.retry === null ? null : providerRetrySummary(binding, `yui task role session retry ${run.taskId} ${run.roleName} show --json`) };
|
|
137
|
+
}
|
|
138
|
+
function summarizeAttention(store, taskId) {
|
|
122
139
|
const group = () => ({ count: 0, refs: [], omittedRefs: 0 });
|
|
123
140
|
const attention = {
|
|
124
141
|
openInputs: group(), pendingOperations: group(), unknownOperations: group(), messages: group()
|
|
@@ -131,7 +148,7 @@ function summarizeAttention(store, taskId, allow) {
|
|
|
131
148
|
]) {
|
|
132
149
|
const page = store.queryContextRecords(taskId, {
|
|
133
150
|
family, statuses, descending: category === "messages",
|
|
134
|
-
unhandledMessages: category === "messages",
|
|
151
|
+
unhandledMessages: category === "messages", limit: MAX_ATTENTION_REFS
|
|
135
152
|
});
|
|
136
153
|
const summary = attention[category];
|
|
137
154
|
summary.count = page.count;
|
|
@@ -154,7 +171,7 @@ function summarizeAttention(store, taskId, allow) {
|
|
|
154
171
|
*/
|
|
155
172
|
export function readTaskContextDelta(store, taskId, input, environment = {}) {
|
|
156
173
|
return store.readTransaction((reader) => {
|
|
157
|
-
|
|
174
|
+
authorizeContext(reader, taskId, environment);
|
|
158
175
|
const start = decode(input.after, taskId);
|
|
159
176
|
const now = currentCursor(reader, taskId);
|
|
160
177
|
const continuation = input.continuation === undefined ? undefined : decodePage(input.continuation, taskId);
|
|
@@ -174,7 +191,7 @@ export function readTaskContextDelta(store, taskId, input, environment = {}) {
|
|
|
174
191
|
throw usageError(`Context delta limit must be between 1 and ${MAX_EVENTS}.`);
|
|
175
192
|
}
|
|
176
193
|
const selected = reader.queryContextRecords(taskId, {
|
|
177
|
-
family: "task-event",
|
|
194
|
+
family: "task-event",
|
|
178
195
|
afterSequence: after, throughSequence: bound.sequence, limit
|
|
179
196
|
});
|
|
180
197
|
const page = [];
|
|
@@ -209,12 +226,48 @@ export function inspectTaskContext(store, taskId, selector, environment = {}) {
|
|
|
209
226
|
const response = readTaskContextResource(store, taskId, selector, environment);
|
|
210
227
|
return boundedDocument(response, `task:${taskId}/${selector.store}/${selector.refId}`, selector.cursor);
|
|
211
228
|
}
|
|
229
|
+
/** CLI spelling shared by explicit dispatch selection and grouped reading. */
|
|
230
|
+
export function parseExactContextRef(selector) {
|
|
231
|
+
const match = /^([^/]+)\/(.+)@([0-9a-f]{64})$/u.exec(selector);
|
|
232
|
+
if (match === null)
|
|
233
|
+
throw usageError(`Invalid required Context reference: ${selector}. Use store/refId@digest from the applicable Context pointers or list/inspect.`);
|
|
234
|
+
return { store: match[1], refId: match[2], digest: match[3] };
|
|
235
|
+
}
|
|
236
|
+
/** Explicit independent originals, one shared transport budget. Reuse the
|
|
237
|
+
* single-reference authorization/digest checks on every page. No implicit
|
|
238
|
+
* discovery, partial success, cached values or read/handling receipts.
|
|
239
|
+
*/
|
|
240
|
+
export function inspectTaskContextBatch(store, taskId, selectors, cursor, environment = {}) {
|
|
241
|
+
if (!Array.isArray(selectors) || selectors.length < 1 || selectors.length > 8)
|
|
242
|
+
throw usageError("Context batch requires 1..8 exact references.");
|
|
243
|
+
const seen = new Set();
|
|
244
|
+
const refs = selectors.map((value) => {
|
|
245
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
246
|
+
throw usageError("Context batch reference requires store, refId and digest.");
|
|
247
|
+
const ref = value;
|
|
248
|
+
if (Object.keys(ref).some(key => !["store", "refId", "digest", "revision"].includes(key))
|
|
249
|
+
|| typeof ref.store !== "string" || !ref.store
|
|
250
|
+
|| typeof ref.refId !== "string" || !ref.refId
|
|
251
|
+
|| typeof ref.digest !== "string" || !/^[a-f0-9]{64}$/.test(ref.digest)
|
|
252
|
+
|| ref.revision !== undefined && typeof ref.revision !== "string")
|
|
253
|
+
throw usageError("Context batch reference requires exact store, refId and digest; revision is optional metadata.");
|
|
254
|
+
const key = JSON.stringify([ref.store, ref.refId]);
|
|
255
|
+
if (seen.has(key))
|
|
256
|
+
throw usageError("Duplicate Context batch reference.");
|
|
257
|
+
seen.add(key);
|
|
258
|
+
return { store: ref.store, refId: ref.refId, digest: ref.digest };
|
|
259
|
+
});
|
|
260
|
+
// All-or-error: a stale/forbidden source cannot be silently skipped. Each
|
|
261
|
+
// source is independently digest-pinned; this is not a new cross-record
|
|
262
|
+
// snapshot. A continuation is additionally bound to the complete response.
|
|
263
|
+
const items = refs.map(ref => readTaskContextResource(store, taskId, ref, environment));
|
|
264
|
+
return boundedDocument({ taskId, items }, `task:${taskId}/context-batch:${readDigest(refs)}`, cursor);
|
|
265
|
+
}
|
|
212
266
|
/** The same authorized read as inspect, before transport pagination. */
|
|
213
267
|
export function readTaskContextResource(store, taskId, selector, environment = {}) {
|
|
214
268
|
return store.readTransaction((reader) => {
|
|
215
269
|
const scope = authorizeContext(reader, taskId, environment);
|
|
216
|
-
const value =
|
|
217
|
-
? inspectValue(reader, scope, selector) : null;
|
|
270
|
+
const value = inspectValue(reader, scope, selector);
|
|
218
271
|
if (value === null)
|
|
219
272
|
throw usageError("Context reference is unavailable in the caller's current scope.");
|
|
220
273
|
const entry = materialize(selector.store, selector.refId, value);
|
|
@@ -259,7 +312,7 @@ export function listTaskContext(store, taskId, family, options = {}, environment
|
|
|
259
312
|
|| options.workItemId !== undefined && value.workItemId !== options.workItemId
|
|
260
313
|
|| options.after !== undefined && Date.parse(String(value.createdAt)) <= Date.parse(options.after))
|
|
261
314
|
continue;
|
|
262
|
-
records.push({ ref: entry.ref, summary:
|
|
315
|
+
records.push({ ref: entry.ref, summary: summarizeEntry(reader, entry),
|
|
263
316
|
...(retired === undefined ? {} : { history: retired.has(entry.ref.refId) ? "retired" : "active" }),
|
|
264
317
|
...Object.fromEntries(["status", "kind", "type", "createdAt", "roleName", "assignee", "purpose", "workItemId", "reviewRoundId"]
|
|
265
318
|
.filter(key => typeof value[key] === "string").map(key => [key, value[key]])) });
|
|
@@ -271,18 +324,10 @@ export function listTaskContext(store, taskId, family, options = {}, environment
|
|
|
271
324
|
})}`, options);
|
|
272
325
|
});
|
|
273
326
|
}
|
|
274
|
-
export const CONTEXT_FAMILIES = [
|
|
275
|
-
"task", "task-brief", "remote-delivery", "role", "role-profile",
|
|
276
|
-
"project-policy", "project-knowledge", "input-request", "work-item",
|
|
277
|
-
"accepted-work-item", "candidate", "environment-preparation", "managed-workspace",
|
|
278
|
-
"change-set", "task-message", "run", "source-run", "review-round", "mailbox",
|
|
279
|
-
"task-decision", "task-milestone", "job", "publication", "task-event"
|
|
280
|
-
];
|
|
281
327
|
export function listContextMessages(store, taskId, environment = {}) {
|
|
282
328
|
return store.readTransaction((reader) => {
|
|
283
|
-
|
|
329
|
+
authorizeContext(reader, taskId, environment);
|
|
284
330
|
return reader.listMessages(taskId)
|
|
285
|
-
.filter((message) => isAllowed(allow, "task-message", message.id))
|
|
286
331
|
.sort((a, b) => a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id));
|
|
287
332
|
});
|
|
288
333
|
}
|
|
@@ -334,7 +379,11 @@ export async function withContextObservations(core, providers, timeoutMs = 250)
|
|
|
334
379
|
}
|
|
335
380
|
return { ...result, omittedObservations: observations.length - result.observations.length };
|
|
336
381
|
}
|
|
337
|
-
/**
|
|
382
|
+
/** Authenticate ordinary reads of the local user's resource. Task/Assignment
|
|
383
|
+
* ownership is execution scope, not a confidentiality boundary. This does not
|
|
384
|
+
* switch Home, impersonate a target Role, or relax explicit global Job limits.
|
|
385
|
+
* Filesystem access and the authenticated Web/Controller transport remain the
|
|
386
|
+
* resource boundary of this single-user local product. */
|
|
338
387
|
export function resolveContextReader(store, environment) {
|
|
339
388
|
const caller = resolveManagedTaskReader(store, environment);
|
|
340
389
|
if (caller === undefined && environment.YUI_SESSION_SCOPE === "global" && environment.YUI_ROLE !== "operator") {
|
|
@@ -351,57 +400,14 @@ export function resolveContextReader(store, environment) {
|
|
|
351
400
|
}
|
|
352
401
|
function authorizeContext(store, taskId, environment) {
|
|
353
402
|
const caller = resolveContextReader(store, environment);
|
|
354
|
-
if (caller !== undefined && caller.taskId !== taskId)
|
|
355
|
-
throw usageError("Context is outside the caller's Task.");
|
|
356
403
|
const task = store.getTask(taskId);
|
|
357
404
|
if (task === null)
|
|
358
405
|
throw taskNotFound(taskId);
|
|
359
|
-
|
|
360
|
-
if (caller !== undefined && caller.roleName !== "leader") {
|
|
361
|
-
if (caller.currentRunId === undefined)
|
|
362
|
-
throw usageError("A managed Role needs a current AgentRun to read its scoped Context.");
|
|
363
|
-
const pack = buildRunContextPack(store, taskId, caller.currentRunId);
|
|
364
|
-
allow = new Set(pack.pointers.map((ref) => `${ref.store}:${ref.refId}`));
|
|
365
|
-
allow.add(`role:${caller.roleName}`);
|
|
366
|
-
allow.add(`turn:${caller.currentRunId}`);
|
|
367
|
-
// Shared current user intent and this exact Assignment's live steers augment
|
|
368
|
-
// the frozen refs. Query identities only: another Role's results, ordinary
|
|
369
|
-
// addressed messages and Leader steers remain outside this scope.
|
|
370
|
-
const run = store.getRun(taskId, caller.currentRunId);
|
|
371
|
-
const current = store.contextInputReferences(taskId, {
|
|
372
|
-
roleName: caller.roleName, workItemId: run?.workItemId, reviewRoundId: run?.reviewRoundId
|
|
373
|
-
});
|
|
374
|
-
for (const id of current.messages)
|
|
375
|
-
allow.add(`task-message:${id}`);
|
|
376
|
-
for (const id of current.events)
|
|
377
|
-
allow.add(`task-event:${id}`);
|
|
378
|
-
if (run?.workItemId !== undefined) {
|
|
379
|
-
for (const job of store.listDurableJobs(taskId)) {
|
|
380
|
-
if (job.owner.kind === "work-item" && job.owner.workItemId === run.workItemId)
|
|
381
|
-
allow.add(`job:${job.id}`);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
return { task, allow, caller };
|
|
386
|
-
}
|
|
387
|
-
/** Independent record commands share Context's exact read boundary, not a
|
|
388
|
-
* second role-based approximation of Assignment visibility. */
|
|
389
|
-
export function contextRecordReader(store, taskId, environment = {}) {
|
|
390
|
-
const { allow } = authorizeContext(store, taskId, environment);
|
|
391
|
-
return (family, id) => isAllowed(allow, family, id);
|
|
392
|
-
}
|
|
393
|
-
export function assertContextRecordReadable(store, taskId, family, id, environment = {}) {
|
|
394
|
-
if (!contextRecordReader(store, taskId, environment)(family, id)) {
|
|
395
|
-
throw usageError("Context reference is outside the caller's Assignment.");
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
function isAllowed(allow, family, id) {
|
|
399
|
-
return allow === undefined || allow.has(`${family}:${id}`);
|
|
406
|
+
return { task, caller };
|
|
400
407
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
.filter(ref => ref.startsWith(prefix)).map(ref => ref.slice(prefix.length));
|
|
408
|
+
/** Independent record commands authenticate against the same resource boundary. */
|
|
409
|
+
export function assertTaskReadable(store, taskId, environment = {}) {
|
|
410
|
+
authorizeContext(store, taskId, environment);
|
|
405
411
|
}
|
|
406
412
|
function roleProfile(role) {
|
|
407
413
|
return {
|
|
@@ -413,12 +419,12 @@ function roleProfile(role) {
|
|
|
413
419
|
/** Resolve just the requested family. Families without a singular Store read
|
|
414
420
|
* use their existing list primitive, without constructing unrelated Context.
|
|
415
421
|
*/
|
|
416
|
-
function inspectValue(store, { task,
|
|
422
|
+
function inspectValue(store, { task, caller }, { store: family, refId }) {
|
|
417
423
|
const taskId = task.id;
|
|
418
424
|
switch (family) {
|
|
419
425
|
case "task": return refId === taskId ? task : null;
|
|
420
426
|
case "task-brief": return refId === taskId ? store.getTaskBrief(taskId) : null;
|
|
421
|
-
case "remote-delivery": return refId === taskId
|
|
427
|
+
case "remote-delivery": return refId === taskId
|
|
422
428
|
? projectTaskRemoteDeliveryFromStore(store, task) : null;
|
|
423
429
|
case "role": return store.getRole(taskId, refId);
|
|
424
430
|
case "role-profile": {
|
|
@@ -442,8 +448,10 @@ function inspectValue(store, { task, allow, caller }, { store: family, refId })
|
|
|
442
448
|
}
|
|
443
449
|
case "input-request": return store.getInputRequest(taskId, refId);
|
|
444
450
|
case "work-item": return store.getWorkItem(taskId, refId);
|
|
445
|
-
case "accepted-work-item":
|
|
446
|
-
|
|
451
|
+
case "accepted-work-item": {
|
|
452
|
+
const item = store.getWorkItem(taskId, refId);
|
|
453
|
+
return item?.status === "accepted" ? item : null;
|
|
454
|
+
}
|
|
447
455
|
case "candidate": {
|
|
448
456
|
const parts = refId.split("/");
|
|
449
457
|
if (parts.length !== 2)
|
|
@@ -457,8 +465,6 @@ function inspectValue(store, { task, allow, caller }, { store: family, refId })
|
|
|
457
465
|
return store.queryContextRecords(taskId, { family, ids: [refId], limit: 1 }).records[0]?.value ?? null;
|
|
458
466
|
case "run": return store.getRun(taskId, refId);
|
|
459
467
|
case "source-run": {
|
|
460
|
-
if (!allow?.has(`source-turn:${refId}`))
|
|
461
|
-
return null;
|
|
462
468
|
const run = store.getRun(taskId, refId);
|
|
463
469
|
return run === null ? null : sourceRunContextValue(run);
|
|
464
470
|
}
|
|
@@ -488,15 +494,13 @@ function inspectValue(store, { task, allow, caller }, { store: family, refId })
|
|
|
488
494
|
case "managed-workspace": {
|
|
489
495
|
// Existing frozen AgentRun overlays use the Task/Role alias; owner keys
|
|
490
496
|
// identify the durable workspace in current Task reads.
|
|
491
|
-
if (caller?.currentRunId !== undefined && refId === `${taskId}/${caller.roleName}`) {
|
|
497
|
+
if (caller?.taskId === taskId && caller.currentRunId !== undefined && refId === `${taskId}/${caller.roleName}`) {
|
|
492
498
|
return store.getRun(taskId, caller.currentRunId)?.workspace ?? null;
|
|
493
499
|
}
|
|
494
500
|
return store.listManagedWorkspaces(taskId)
|
|
495
501
|
.find((workspace) => managedWorkspaceKey(workspace.owner) === refId) ?? null;
|
|
496
502
|
}
|
|
497
503
|
case "mailbox": {
|
|
498
|
-
if (allow !== undefined)
|
|
499
|
-
return null;
|
|
500
504
|
if (refId === "task")
|
|
501
505
|
return store.getWorkMailbox({ kind: "task", taskId });
|
|
502
506
|
const roleName = refId.startsWith("role:") ? refId.slice("role:".length) : undefined;
|
|
@@ -509,29 +513,39 @@ function inspectValue(store, { task, allow, caller }, { store: family, refId })
|
|
|
509
513
|
default: return null;
|
|
510
514
|
}
|
|
511
515
|
}
|
|
512
|
-
function authorizedSources(store, { task,
|
|
516
|
+
function authorizedSources(store, { task, caller }, current = false, requestedFamily, discovery = false) {
|
|
513
517
|
const taskId = task.id;
|
|
514
518
|
const sources = [];
|
|
515
519
|
const add = (family, id, value) => {
|
|
516
|
-
if ((requestedFamily === undefined || requestedFamily === family) && value !== null
|
|
520
|
+
if ((requestedFamily === undefined || requestedFamily === family) && value !== null) {
|
|
517
521
|
sources.push({ family, count: 1, read: () => [materialize(family, id, value)] });
|
|
518
522
|
}
|
|
519
523
|
};
|
|
520
524
|
const query = (family, options = {}) => {
|
|
521
525
|
if (requestedFamily !== undefined && requestedFamily !== family)
|
|
522
526
|
return;
|
|
523
|
-
const selection = { ...options, family
|
|
527
|
+
const selection = { ...options, family };
|
|
524
528
|
const { count } = store.queryContextRecords(taskId, { ...selection, limit: 0 });
|
|
529
|
+
// The bounded current index must not bury Task main behind alphabetically
|
|
530
|
+
// earlier integration/review history. Full discovery keeps its stable order.
|
|
531
|
+
const taskWorkspaceKey = managedWorkspaceKey({ type: "task", taskId });
|
|
532
|
+
const preferredWorkspace = current && !discovery && family === "managed-workspace"
|
|
533
|
+
? store.getManagedWorkspace({ type: "task", taskId }) : null;
|
|
525
534
|
sources.push({ family, count, *read(offset = 0) {
|
|
535
|
+
if (preferredWorkspace !== null)
|
|
536
|
+
yield materialize(family, taskWorkspaceKey, preferredWorkspace);
|
|
526
537
|
for (const { id, value } of store.queryContextRecords(taskId, {
|
|
527
538
|
...selection, offset, limit: current && !discovery ? 8 : MAX_RECORDS
|
|
528
|
-
}).records)
|
|
539
|
+
}).records) {
|
|
540
|
+
if (preferredWorkspace !== null && id === taskWorkspaceKey)
|
|
541
|
+
continue;
|
|
529
542
|
yield materialize(family, id, value);
|
|
543
|
+
}
|
|
530
544
|
} });
|
|
531
545
|
};
|
|
532
546
|
add("task", taskId, task);
|
|
533
547
|
add("task-brief", taskId, store.getTaskBrief(taskId));
|
|
534
|
-
if (
|
|
548
|
+
if (requestedFamily === undefined || requestedFamily === "remote-delivery") {
|
|
535
549
|
add("remote-delivery", taskId, projectTaskRemoteDeliveryFromStore(store, task));
|
|
536
550
|
}
|
|
537
551
|
for (const role of store.listRoles(taskId)) {
|
|
@@ -552,9 +566,6 @@ function authorizedSources(store, { task, allow, caller }, current = false, requ
|
|
|
552
566
|
// Current responsibilities and unanswered inputs precede historical bulk.
|
|
553
567
|
query("input-request", { statuses: ["open"] });
|
|
554
568
|
query("work-item", current ? { excludeStatuses: ["accepted", "retired"] } : {});
|
|
555
|
-
for (const id of allowedIds(allow, "accepted-work-item") ?? []) {
|
|
556
|
-
add("accepted-work-item", id, store.getWorkItem(taskId, id));
|
|
557
|
-
}
|
|
558
569
|
if (!current)
|
|
559
570
|
query("candidate");
|
|
560
571
|
// File artifacts are not enumerated into the synchronous Context listing: the
|
|
@@ -564,23 +575,21 @@ function authorizedSources(store, { task, allow, caller }, current = false, requ
|
|
|
564
575
|
// artifacts remain reachable as commit-pinned pointers under their Candidate.
|
|
565
576
|
if (!current)
|
|
566
577
|
query("environment-preparation");
|
|
567
|
-
|
|
568
|
-
|
|
578
|
+
if (!current || discovery)
|
|
579
|
+
query("managed-workspace");
|
|
580
|
+
if (caller?.taskId === taskId && caller.currentRunId !== undefined) {
|
|
569
581
|
add("managed-workspace", `${taskId}/${caller.roleName}`, store.getRun(taskId, caller.currentRunId)?.workspace ?? null);
|
|
570
582
|
}
|
|
583
|
+
if (current && !discovery)
|
|
584
|
+
query("managed-workspace");
|
|
571
585
|
if (!current)
|
|
572
586
|
query("change-set");
|
|
573
587
|
query("task-message", { descending: true, unhandledMessages: current });
|
|
574
588
|
query("run", { descending: true, ...(current ? { statuses: ["active"] } : {}) });
|
|
575
|
-
for (const id of allowedIds(allow, "source-turn") ?? []) {
|
|
576
|
-
const run = store.getRun(taskId, id);
|
|
577
|
-
if (run !== null)
|
|
578
|
-
add("source-run", id, sourceRunContextValue(run));
|
|
579
|
-
}
|
|
580
589
|
query("review-round", { descending: true, ...(current ? { statuses: ["pending", "running"] } : {}) });
|
|
581
590
|
if (!current)
|
|
582
591
|
query("input-request", { excludeStatuses: ["open"] });
|
|
583
|
-
if (!current
|
|
592
|
+
if (!current) {
|
|
584
593
|
add("mailbox", "task", store.getWorkMailbox({ kind: "task", taskId }));
|
|
585
594
|
for (const role of store.listRoles(taskId)) {
|
|
586
595
|
add("mailbox", `role:${role.name}`, store.getWorkMailbox({ kind: "role", taskId, roleName: role.name }));
|
|
@@ -646,8 +655,31 @@ function freeze(value) {
|
|
|
646
655
|
}
|
|
647
656
|
function summarize(value) {
|
|
648
657
|
const record = value;
|
|
649
|
-
return summaryText(["title", "name", "activeAgentId", "scope", "expiresWhen", "version", "objective", "question", "summary", "body"
|
|
658
|
+
return summaryText(["title", "name", "status", "type", "currentFocus", "root", "activeAgentId", "scope", "expiresWhen", "version", "objective", "question", "summary", "body"]
|
|
650
659
|
.flatMap((key) => typeof record[key] === "string" || (key === "version" && typeof record[key] === "number")
|
|
651
660
|
? [`${key}: ${record[key]}`] : [])
|
|
652
661
|
.join("; "));
|
|
653
662
|
}
|
|
663
|
+
/** Reuse producer-authored navigation, never generate a verdict or copy a
|
|
664
|
+
* report. The authorized Message already permits its matching terminal result
|
|
665
|
+
* through exact inspect; unrelated/malformed links cannot supply summaries.
|
|
666
|
+
*/
|
|
667
|
+
function summarizeEntry(store, entry) {
|
|
668
|
+
if (entry.ref.store === "task-message") {
|
|
669
|
+
const message = entry.value;
|
|
670
|
+
const presentation = taskMessagePresentation(message, (taskId, runId) => {
|
|
671
|
+
const run = store.getRun(taskId, runId);
|
|
672
|
+
return run !== null && run.taskId === message.taskId
|
|
673
|
+
&& message.author.type === "role" && run.roleName === message.author.roleName
|
|
674
|
+
&& run.status !== "active" && run.result !== undefined ? run : null;
|
|
675
|
+
});
|
|
676
|
+
if (presentation.summary !== undefined) {
|
|
677
|
+
// Include attribution in the existing budget without clipping qualifiers.
|
|
678
|
+
const label = `Author summary (${presentation.summarySource})`;
|
|
679
|
+
const text = `${label}: ${presentation.summary}`;
|
|
680
|
+
return Buffer.byteLength(text) <= 512 ? text
|
|
681
|
+
: `${label} omitted: exceeds discovery budget; read original.`;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return summarize(entry.value);
|
|
685
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { usageError } from "../errors/cliError.js";
|
|
2
2
|
import { TASK_SEARCH_KINDS } from "../storage/taskSearch.js";
|
|
3
3
|
import { contextContentDigest } from "./contextSnapshot.js";
|
|
4
|
-
import {
|
|
5
|
-
import { readTaskContextResource } from "./taskContext.js";
|
|
4
|
+
import { readTaskContextResource, resolveContextReader } from "./taskContext.js";
|
|
6
5
|
const PAGE_BYTES = 32 * 1024;
|
|
7
6
|
/** Human-readable field pages, using the same source authorization and version
|
|
8
7
|
* checks as Context. Offsets are Unicode code points, like search snippets. */
|
|
@@ -67,11 +66,8 @@ export function searchTaskBodies(store, options, environment = {}) {
|
|
|
67
66
|
...(options.kind === undefined ? [] : ["--kind", options.kind]),
|
|
68
67
|
...(options.cursor === undefined ? [] : ["--cursor", options.cursor])]);
|
|
69
68
|
return store.readTransaction(reader => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
throw usageError("Search target is outside the caller's Task.");
|
|
73
|
-
}
|
|
74
|
-
const taskId = callerTask ?? valid.taskId;
|
|
69
|
+
resolveContextReader(reader, environment);
|
|
70
|
+
const taskId = valid.taskId;
|
|
75
71
|
const scope = contextContentDigest({ taskId: taskId ?? null, query: valid.query, kind: valid.kind ?? null });
|
|
76
72
|
const after = valid.cursor === undefined ? undefined : decodeCursor(valid.cursor, scope);
|
|
77
73
|
const rows = reader.queryTaskSearch({ ...valid, taskId, after, limit: valid.limit + 1 });
|
package/dist/controlPlaneCli.js
CHANGED
|
@@ -4,6 +4,7 @@ import { randomUUID } from "node:crypto";
|
|
|
4
4
|
import { readFileSync, realpathSync } from "node:fs";
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
6
|
import { createInterface } from "node:readline/promises";
|
|
7
|
+
import { isatty } from "node:tty";
|
|
7
8
|
import { fileURLToPath } from "node:url";
|
|
8
9
|
import { isDeepStrictEqual } from "node:util";
|
|
9
10
|
import { agentAdapterLabel as adapterLabel } from "./agent/adapterCatalog.js";
|
|
@@ -11,17 +12,17 @@ import { recordTaskInterruptResult } from "./message/taskInterrupt.js";
|
|
|
11
12
|
import { nativeAgentEnvironmentNames } from "./agent/launchEnvironment.js";
|
|
12
13
|
import { listArtifactsCapability, readArtifactCapability, saveArtifactCapability } from "./artifacts/artifactCapability.js";
|
|
13
14
|
import { renderAgentConfigurationResolutionNotice } from "./cli/agentConfigurationPicker.js";
|
|
14
|
-
import { describeCommandTree, findCommandNode } from "./cli/commandCatalog.js";
|
|
15
|
+
import { describeCommandHelp, describeCommandTree, findCommandNode } from "./cli/commandCatalog.js";
|
|
15
16
|
import { discoveryCommandTree } from "./cli/commandDiscovery.js";
|
|
16
17
|
import { callerDiscoveryAudience } from "./cli/callerDiscovery.js";
|
|
17
|
-
import {
|
|
18
|
+
import { assertTaskReadable } from "./context/taskContext.js";
|
|
18
19
|
import { renderCompletion } from "./cli/completion.js";
|
|
19
20
|
import { runCompletionWizard } from "./cli/completionWizard.js";
|
|
20
21
|
import { resolveCompletionCandidates } from "./cli/dynamicCompletion.js";
|
|
21
|
-
import { renderCommandHelp } from "./cli/helpRenderer.js";
|
|
22
|
+
import { renderCommandHelp, renderCommandPathHint } from "./cli/helpRenderer.js";
|
|
22
23
|
import { allowsInteractiveSelection, resolveInteractiveArguments } from "./cli/interactiveSelection.js";
|
|
23
24
|
import { routeInvocation } from "./cli/invocationRouter.js";
|
|
24
|
-
import { operatorOfflineCommand,
|
|
25
|
+
import { operatorOfflineCommand, isTaskReadCommand } from "./cli/managedDiagnostics.js";
|
|
25
26
|
import { assertConfigurationAuthority, assertTaskInvocationScope } from "./cli/invocationAuthority.js";
|
|
26
27
|
import { requireManagedGlobalCaller, requireManagedGlobalReader, ManagedRuntimeDriftError } from "./runtime/managedCaller.js";
|
|
27
28
|
import { assertJobAgentCommand } from "./job/agentRoleScope.js";
|
|
@@ -202,7 +203,7 @@ export async function main() {
|
|
|
202
203
|
return;
|
|
203
204
|
}
|
|
204
205
|
if (args.length === 0) {
|
|
205
|
-
emit(renderCommandHelp(discovery, VERSION), false, jsonOutput ?
|
|
206
|
+
emit(renderCommandHelp(discovery, VERSION), false, jsonOutput ? describeCommandHelp(discovery) : undefined);
|
|
206
207
|
return;
|
|
207
208
|
}
|
|
208
209
|
if (args[0] === "version" && args.length === 1) {
|
|
@@ -211,11 +212,19 @@ export async function main() {
|
|
|
211
212
|
}
|
|
212
213
|
const invocation = routeInvocation(args, discovery);
|
|
213
214
|
if (invocation.kind === "help") {
|
|
214
|
-
|
|
215
|
+
if (invocation.recursive && !jsonOutput)
|
|
216
|
+
throw usageError("Recursive help requires --json.");
|
|
217
|
+
emit(renderCommandHelp(invocation.node, VERSION), true, jsonOutput
|
|
218
|
+
? invocation.recursive ? describeCommandTree(invocation.node) : describeCommandHelp(invocation.node)
|
|
219
|
+
: undefined);
|
|
215
220
|
return;
|
|
216
221
|
}
|
|
217
222
|
if (invocation.kind === "path-error") {
|
|
218
|
-
throw usageError(
|
|
223
|
+
throw usageError(invocation.discoveryOnly
|
|
224
|
+
? `Command not listed in the current caller's help view: ${invocation.typedPath}. `
|
|
225
|
+
+ "Help is filtered discovery, not an execution or permission check. "
|
|
226
|
+
+ "Follow current instructions and exact read links; execution still validates authority."
|
|
227
|
+
: `Unknown command: ${invocation.typedPath}`, renderCommandPathHint(invocation.helpNode));
|
|
219
228
|
}
|
|
220
229
|
if (invocation.kind === "incomplete") {
|
|
221
230
|
throw usageError(`Command required after: ${invocation.typedPath}`, renderCommandHelp(invocation.helpNode, VERSION));
|
|
@@ -660,7 +669,6 @@ export async function main() {
|
|
|
660
669
|
const tmux = new TmuxManager(resolveTmuxBin(store.getConfig().tmuxBin), executor, {
|
|
661
670
|
yuiHome: home,
|
|
662
671
|
historyLimit: resolveTmuxHistoryLimit(store.getConfig().tmuxHistoryLimit),
|
|
663
|
-
terminalInput: process.stdin,
|
|
664
672
|
onWarning: (message) => writeCliWarning(message)
|
|
665
673
|
});
|
|
666
674
|
const telemetry = openSchedulerTelemetry(home, store.getConfig());
|
|
@@ -1094,7 +1102,7 @@ export async function main() {
|
|
|
1094
1102
|
if (taskId === undefined || action === undefined || !["list", "read", "save"].includes(action)) {
|
|
1095
1103
|
throw usageError(usage);
|
|
1096
1104
|
}
|
|
1097
|
-
if (process.env.YUI_SESSION_SCOPE === "task" && process.env.YUI_TASK_ID !== taskId) {
|
|
1105
|
+
if (action === "save" && process.env.YUI_SESSION_SCOPE === "task" && process.env.YUI_TASK_ID !== taskId) {
|
|
1098
1106
|
throw usageError("Artifact is outside the managed Task scope.");
|
|
1099
1107
|
}
|
|
1100
1108
|
if (store.getTask(taskId) === null)
|
|
@@ -2065,12 +2073,7 @@ async function preflightManagedTaskControlPlane() {
|
|
|
2065
2073
|
&& ["agent-host", "session-notify", "runtime-hook"].includes(args[1] ?? "");
|
|
2066
2074
|
const home = resolveYuiHome(process.env);
|
|
2067
2075
|
assertManagedSessionManifest(home, "task");
|
|
2068
|
-
const
|
|
2069
|
-
const diagnostic = diagnosticTarget !== undefined
|
|
2070
|
-
&& diagnosticTarget === process.env.YUI_TASK_ID;
|
|
2071
|
-
if (diagnosticTarget !== undefined && !diagnostic) {
|
|
2072
|
-
throw usageError("Diagnostics must remain within this Session's Task.");
|
|
2073
|
-
}
|
|
2076
|
+
const diagnostic = isTaskReadCommand(args);
|
|
2074
2077
|
// One gate for every managed command: the current CLI, Home, and Controller
|
|
2075
2078
|
// must agree. Internal callbacks must still be able to append their immutable
|
|
2076
2079
|
// fact while the Controller is offline.
|
|
@@ -2087,13 +2090,6 @@ async function preflightManagedTaskControlPlane() {
|
|
|
2087
2090
|
: diagnostic
|
|
2088
2091
|
? resolveManagedTaskReader(verifiedStore, process.env)
|
|
2089
2092
|
: requireManagedTaskCaller(verifiedStore, process.env);
|
|
2090
|
-
if (diagnostic && runtime?.roleName !== "leader"
|
|
2091
|
-
&& !(args[1] === "show"
|
|
2092
|
-
|| (args[1] === "role" && args[2] === "session" && args[3] === "inspect"
|
|
2093
|
-
&& args[5] === runtime?.roleName))) {
|
|
2094
|
-
// Non-Leaders keep their Assignment-scoped read authorization.
|
|
2095
|
-
requireManagedTaskCaller(verifiedStore, process.env);
|
|
2096
|
-
}
|
|
2097
2093
|
const request = taskFinalReviewInvocation.request;
|
|
2098
2094
|
if (request !== undefined && diagnostic)
|
|
2099
2095
|
requireManagedTaskCaller(verifiedStore, process.env);
|
|
@@ -2762,7 +2758,9 @@ function completionShell(value) {
|
|
|
2762
2758
|
throw usageError("Completion shell must be one of bash, zsh, fish.");
|
|
2763
2759
|
}
|
|
2764
2760
|
async function resolveTerminalArguments(commandArgs, node, store, catalogs) {
|
|
2765
|
-
|
|
2761
|
+
// Probe the descriptor without constructing a stdin stream: Node makes
|
|
2762
|
+
// pipes nonblocking on stream creation, breaking later --body-file - reads.
|
|
2763
|
+
const interactive = isatty(0) && process.stdout.isTTY === true;
|
|
2766
2764
|
if (!interactive || !allowsInteractiveSelection(commandArgs, jsonOutput)) {
|
|
2767
2765
|
return [...commandArgs];
|
|
2768
2766
|
}
|
|
@@ -3079,7 +3077,7 @@ function completionSelectionPorts(home) {
|
|
|
3079
3077
|
if (process.env.YUI_SESSION_SCOPE !== "task")
|
|
3080
3078
|
return ports;
|
|
3081
3079
|
const taskId = process.env.YUI_TASK_ID;
|
|
3082
|
-
|
|
3080
|
+
assertTaskReadable(store, taskId, process.env);
|
|
3083
3081
|
const families = {
|
|
3084
3082
|
"task.list": "task", "project.list": "project-policy", "task.role.list": "role",
|
|
3085
3083
|
"task.work.list": "work-item", "task.message.list": "task-message", "task.input.list": "input-request",
|
|
@@ -3103,7 +3101,7 @@ function completionSelectionPorts(home) {
|
|
|
3103
3101
|
return false;
|
|
3104
3102
|
if (method === "project.list" && !store.getTask(taskId)?.projectBindings.some(binding => binding.projectId === id))
|
|
3105
3103
|
return false;
|
|
3106
|
-
return
|
|
3104
|
+
return true;
|
|
3107
3105
|
});
|
|
3108
3106
|
} };
|
|
3109
3107
|
}
|