@zq-silk/yui 2.0.0 → 2.2.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 +4 -0
- package/dist/cli/commandCatalog.js +318 -289
- package/dist/cli/commandDiscovery.js +72 -0
- package/dist/cli/completion.js +22 -227
- package/dist/cli/dynamicCompletion.js +12 -6
- package/dist/cli/invocationAuthority.js +2 -2
- package/dist/cli/invocationRouter.js +18 -7
- package/dist/cli.js +23 -3030
- package/dist/commands/capabilityCommands.js +5 -5
- package/dist/commands/globalRoleCommands.js +2 -1
- package/dist/commands/taskCommands.js +90 -21
- package/dist/commands/taskIntegrationCommands.js +3 -1
- package/dist/commands/taskUpstreamCommands.js +3 -1
- package/dist/context/runContextPack.js +132 -5
- package/dist/context/sourceRunContext.js +4 -2
- package/dist/context/taskContext.js +12 -3
- package/dist/controlPlaneCli.js +3092 -0
- package/dist/controller/fileSchedulerStoreAdapter.js +19 -6
- package/dist/controller/jobControl.js +21 -0
- package/dist/executor/agentExecutor.js +1 -1
- package/dist/executor/effectiveLaunch.js +18 -5
- package/dist/executor/fileRoleLaunchPlanner.js +15 -19
- package/dist/integration/gitIntegrationService.js +50 -15
- package/dist/kernel/accessAssessment.js +1 -0
- package/dist/kernel/builtinCapabilities.js +28 -1
- package/dist/kernel/capabilityRegistry.js +64 -19
- package/dist/message/messageContinuation.js +7 -2
- package/dist/nativeAgent/agent.js +275 -0
- package/dist/nativeAgent/cliDemo.js +37 -0
- package/dist/nativeAgent/codingTools.js +11 -0
- package/dist/nativeAgent/commandTool.js +215 -0
- package/dist/nativeAgent/compactionDemo.js +158 -0
- package/dist/nativeAgent/composition.js +71 -0
- package/dist/nativeAgent/context/budget.js +44 -0
- package/dist/nativeAgent/context/index.js +339 -0
- package/dist/nativeAgent/context/providerCompressor.js +103 -0
- package/dist/nativeAgent/contracts.js +1 -0
- package/dist/nativeAgent/demo.js +40 -0
- package/dist/nativeAgent/evaluation/cases.js +38 -0
- package/dist/nativeAgent/evaluation/checks.js +91 -0
- package/dist/nativeAgent/evaluation/demo.js +19 -0
- package/dist/nativeAgent/evaluation/files.js +54 -0
- package/dist/nativeAgent/evaluation/fixture.js +36 -0
- package/dist/nativeAgent/evaluation/index.js +239 -0
- package/dist/nativeAgent/executionOwner.js +209 -0
- package/dist/nativeAgent/filePatterns.js +170 -0
- package/dist/nativeAgent/fileToolsSupport.js +202 -0
- package/dist/nativeAgent/index.js +16 -0
- package/dist/nativeAgent/interaction/cli.js +358 -0
- package/dist/nativeAgent/interaction/contracts.js +1 -0
- package/dist/nativeAgent/interaction/index.js +3 -0
- package/dist/nativeAgent/interaction/memoryDemo.js +115 -0
- package/dist/nativeAgent/interaction/renderer.js +34 -0
- package/dist/nativeAgent/localSafety.js +258 -0
- package/dist/nativeAgent/mockProvider.js +43 -0
- package/dist/nativeAgent/model/anthropicMessages.js +204 -0
- package/dist/nativeAgent/model/chatCompletions.js +210 -0
- package/dist/nativeAgent/model/errors.js +47 -0
- package/dist/nativeAgent/model/gateway.js +423 -0
- package/dist/nativeAgent/model/index.js +7 -0
- package/dist/nativeAgent/model/observationAdapter.js +21 -0
- package/dist/nativeAgent/model/protocols.js +19 -0
- package/dist/nativeAgent/model/responses.js +263 -0
- package/dist/nativeAgent/model/types.js +1 -0
- package/dist/nativeAgent/model/wire.js +73 -0
- package/dist/nativeAgent/observability/index.js +220 -0
- package/dist/nativeAgent/product/catalog.js +82 -0
- package/dist/nativeAgent/product/config.js +295 -0
- package/dist/nativeAgent/product/facts.js +30 -0
- package/dist/nativeAgent/product/index.js +62 -0
- package/dist/nativeAgent/product/location.js +44 -0
- package/dist/nativeAgent/product/runtime.js +276 -0
- package/dist/nativeAgent/product/storage.js +49 -0
- package/dist/nativeAgent/product/tools.js +47 -0
- package/dist/nativeAgent/product/transport.js +54 -0
- package/dist/nativeAgent/projectGuidance/index.js +425 -0
- package/dist/nativeAgent/searchTools.js +305 -0
- package/dist/nativeAgent/session/backends.js +293 -0
- package/dist/nativeAgent/session/catalog.js +97 -0
- package/dist/nativeAgent/session/catalogDemo.js +87 -0
- package/dist/nativeAgent/session/contracts.js +1 -0
- package/dist/nativeAgent/session/format.js +269 -0
- package/dist/nativeAgent/session/index.js +5 -0
- package/dist/nativeAgent/session/location.js +36 -0
- package/dist/nativeAgent/session/sqliteFormat.js +134 -0
- package/dist/nativeAgent/session/store.js +248 -0
- package/dist/nativeAgent/textTools.js +293 -0
- package/dist/nativeAgent/toolManager/executor.js +290 -0
- package/dist/nativeAgent/toolManager/index.js +4 -0
- package/dist/nativeAgent/validation.js +95 -0
- package/dist/plugins/pluginService.js +98 -8
- package/dist/resources/projectResourceService.js +23 -1
- package/dist/runtime/managedIdentity.js +6 -0
- package/dist/surface/surfaceContributions.js +3 -0
- package/dist/task/taskAuthority.js +61 -10
- package/dist/web/assets/assetManifest.js +38 -22
- package/dist/web/assets/client/api.js +115 -0
- package/dist/web/assets/client/app.js +615 -862
- package/dist/web/assets/client/components.js +323 -897
- package/dist/web/assets/client/detail.js +333 -0
- package/dist/web/assets/client/dock.js +225 -0
- package/dist/web/assets/client/dom.js +55 -3
- package/dist/web/assets/client/evidence.js +172 -0
- package/dist/web/assets/client/format.js +25 -14
- package/dist/web/assets/client/forms.js +230 -0
- package/dist/web/assets/client/i18n.js +1024 -791
- package/dist/web/assets/client/overview.js +118 -0
- package/dist/web/assets/client/records.js +63 -0
- package/dist/web/assets/client/sections.js +313 -0
- package/dist/web/assets/client/sidebar.js +146 -0
- package/dist/web/assets/client/theme.js +61 -22
- package/dist/web/assets/icons.js +39 -0
- package/dist/web/assets/shell.js +125 -118
- package/dist/web/assets/styles/base.js +41 -0
- package/dist/web/assets/styles/components.js +169 -0
- package/dist/web/assets/styles/layout.js +56 -77
- package/dist/web/assets/styles/markdown.js +16 -24
- package/dist/web/assets/styles/responsive.js +41 -44
- package/dist/web/assets/styles/tokens.js +65 -89
- package/dist/web/assets/styles/views.js +292 -0
- package/dist/web/webServer.js +2 -1
- package/docs/agent-result-consumption.md +16 -0
- package/docs/agent-result-consumption.zh-CN.md +13 -0
- package/docs/examples/agent-offline.mjs +194 -0
- package/docs/native-agent.md +283 -0
- package/docs/release-workflow.md +61 -0
- package/docs/release-workflow.zh-CN.md +46 -0
- package/docs/roles-and-configuration.md +32 -0
- package/docs/roles-and-configuration.zh-CN.md +26 -0
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +9 -0
- package/skills/yui-reviewer/SKILL.md +5 -0
- package/skills/yui-runtime/SKILL.md +33 -0
- package/dist/web/assets/client/taskSummary.js +0 -350
- package/dist/web/assets/client/taskSurface.js +0 -616
- package/dist/web/assets/client/view.js +0 -608
- package/dist/web/assets/styles/cards.js +0 -247
- package/dist/web/assets/styles/widgets.js +0 -168
|
@@ -3,7 +3,7 @@ import { resolveJobCaller } from "../task/taskAuthority.js";
|
|
|
3
3
|
import { callFileTaskController } from "../controller/clientRuntime.js";
|
|
4
4
|
export async function runCapabilityCommand(args, home, environment) {
|
|
5
5
|
const [action, ...rest] = args;
|
|
6
|
-
const usage = "yui capability search [query] --task <id> | commands --task <id> | panels --task <id> | describe <name> --task <id> [--provider <id>] [--version <version>] | call <name> --task <id> --input <json> [--provider <id>] [--version <version>] [--request-id <id>]";
|
|
6
|
+
const usage = "yui capability search [query] --task <id> | commands --task <id> | panels --task <id> | describe <name> --task <id> [--input <json>] [--provider <id>] [--version <version>] | call <name> --task <id> --input <json> [--provider <id>] [--version <version>] [--request-id <id>]";
|
|
7
7
|
if (!["search", "commands", "panels", "describe", "call"].includes(action))
|
|
8
8
|
throw usageError(usage);
|
|
9
9
|
const surfaceList = action === "commands" || action === "panels";
|
|
@@ -29,12 +29,12 @@ export async function runCapabilityCommand(args, home, environment) {
|
|
|
29
29
|
throw usageError(usage);
|
|
30
30
|
if ((action === "search" || surfaceList) && [...flags.keys()].some((key) => key !== "--task"))
|
|
31
31
|
throw usageError(usage);
|
|
32
|
-
if (action === "describe" &&
|
|
32
|
+
if (action === "describe" && flags.has("--request-id"))
|
|
33
33
|
throw usageError(usage);
|
|
34
34
|
let input;
|
|
35
|
-
if (action === "call")
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
if (action === "call" && !flags.has("--input"))
|
|
36
|
+
throw usageError("Capability call requires --input <json>.", usage);
|
|
37
|
+
if (flags.has("--input")) {
|
|
38
38
|
try {
|
|
39
39
|
input = JSON.parse(flags.get("--input"));
|
|
40
40
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { roleNotFound, usageError } from "../errors/cliError.js";
|
|
2
|
+
import { hasManagedIdentity } from "../runtime/managedIdentity.js";
|
|
2
3
|
import { boundedDocument, recordPage, readOptions, readDigest, messageReceipt } from "../output/boundedRead.js";
|
|
3
4
|
import { controlProviderRetry, providerRetryProjection, renderProviderRetry } from "../runtime/providerRetry.js";
|
|
4
5
|
import { settleGlobalRetryInput } from "../message/globalProviderRetry.js";
|
|
@@ -50,7 +51,7 @@ function assertGlobalInputAuthority(store, options, target) {
|
|
|
50
51
|
throw usageError("Global input mutation requires the current native caller Session.");
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
|
-
else if (env
|
|
54
|
+
else if (hasManagedIdentity(env)) {
|
|
54
55
|
throw usageError("Incomplete managed identity cannot acquire user authority.");
|
|
55
56
|
}
|
|
56
57
|
}
|
|
@@ -4,11 +4,12 @@ import { agentExecutionComponentLabel } from "../agent/executionComponents.js";
|
|
|
4
4
|
import { assertLeaderArchiveAdmission } from "../task/leaderArchiveAuthority.js";
|
|
5
5
|
import { createRun, runExecutionObservation, runPurposeAdmitsTaskState } from "../agentRun/agentRun.js";
|
|
6
6
|
import { isGitArtifactRefString, parseGitArtifactRef } from "../artifacts/gitArtifactRef.js";
|
|
7
|
-
import { contextSnapshotRef } from "../context/contextSnapshot.js";
|
|
8
|
-
import {
|
|
7
|
+
import { contextContentDigest, contextSnapshotRef } from "../context/contextSnapshot.js";
|
|
8
|
+
import { sourceRunContextValue } from "../context/sourceRunContext.js";
|
|
9
|
+
import { buildRunContextDelta, buildRunContextPack, contextSnapshotDeltaRefIds, expandRunContextRef, freezeReviewStageContextSnapshot, freezeRunContextSnapshot, freezeWorkItemExecutionAssignmentContextSnapshot, freezeCandidateContextSnapshot, readRunContextSnapshot, synthesisSourceRunIds } from "../context/runContextPack.js";
|
|
9
10
|
import { createRunInput } from "../context/runInputContract.js";
|
|
10
11
|
import { parseTaskCatalogOptions, readTaskCatalog, renderTaskCatalog } from "../context/taskCatalog.js";
|
|
11
|
-
import { assertContextRecordReadable } from "../context/taskContext.js";
|
|
12
|
+
import { assertContextRecordReadable, readTaskContextResource } from "../context/taskContext.js";
|
|
12
13
|
import { boundedDocument, readOptions, messageReceipt, recordPage } from "../output/boundedRead.js";
|
|
13
14
|
import { referencedWakeRunIds } from "../context/wakeRunReferences.js";
|
|
14
15
|
import { completeProcessing, releaseProcessing } from "../coordination/workMailbox.js";
|
|
@@ -19,7 +20,7 @@ import { createExecutionGroup, createReviewExecutionAssignment, createWorkItemEx
|
|
|
19
20
|
import { projectWorkItemExecution } from "../execution/workItemExecutionProjection.js";
|
|
20
21
|
import { dispatchWorkItemSynthesis, selectedWorkItemSynthesisProducers } from "../execution/workItemMainRun.js";
|
|
21
22
|
import { createRoleSessionSet, roleAgentSessionResumeMode, roleSessionControlTarget, updateTaskRoleProviderRuntime } from "../executor/agentExecutor.js";
|
|
22
|
-
import { resolveEffectiveLaunch } from "../executor/effectiveLaunch.js";
|
|
23
|
+
import { resolveEffectiveLaunch, effectiveLaunchWithTaskMainWorkspace } from "../executor/effectiveLaunch.js";
|
|
23
24
|
import { cancelInputRequest } from "../input/inputRequest.js";
|
|
24
25
|
import { retireExactActiveRun, terminalizeExactTaskRun, validateExactRunReviewRound } from "../lifecycle/exactRunTerminalization.js";
|
|
25
26
|
import { resolveTaskInputControl } from "../message/inputControlResolution.js";
|
|
@@ -72,6 +73,8 @@ import { runGrantCommand } from "./grantCommands.js";
|
|
|
72
73
|
import { hasAgentConfigOptions, hasNoRoleMutation, parseRoleOptions, patchRoleAgentBinding, roleOptionSpecs, roleProfilePatch } from "./roleConfiguration.js";
|
|
73
74
|
import { assertRoleRuntimeMutationAllowed } from "./roleRuntimeGuard.js";
|
|
74
75
|
import { hasRoleLaunchContextOptions, validateConfiguredRoleSkills } from "./roleSkillValidation.js";
|
|
76
|
+
import { requireManagedGlobalCaller } from "../runtime/managedCaller.js";
|
|
77
|
+
import { hasManagedIdentity } from "../runtime/managedIdentity.js";
|
|
75
78
|
import { nonLeaderActivationIdentity, runTaskActivationCommand } from "./taskActivationCommands.js";
|
|
76
79
|
import { assertTaskOpen, clock, exactPositionals, leaderActionEventPayload, leaderMailbox, notifyMailbox, optionalNonEmptyOption, output, parseMultiValueTail, parseTail, presentTime, recordTaskEvent, recordTaskEventRecord, requiredOption, requiredText, requireTask, roleMailbox, taskActor, taskMailbox, taskRef, trimmed } from "./taskCommandSupport.js";
|
|
77
80
|
import { assertTaskCompletionPublishedTreeProof } from "./taskCompletionGate.js";
|
|
@@ -791,6 +794,15 @@ function parseSubmissionIntentOption(raw, usage) {
|
|
|
791
794
|
throw usageError(`--intent must be one of ${TASK_SUBMISSION_INTENTS.join(", ")}: ${raw}.`, usage);
|
|
792
795
|
}
|
|
793
796
|
export function submitOperatorMessage(body, taskId, store, options = {}, intent, submissionKey) {
|
|
797
|
+
// Public terminals and ordinary unmanaged Agents retain the submission
|
|
798
|
+
// route. Managed callers cannot turn a Task Assignment into Operator input.
|
|
799
|
+
const callerEnvironment = options.environment ?? {};
|
|
800
|
+
if (hasManagedIdentity(callerEnvironment)) {
|
|
801
|
+
if (callerEnvironment.YUI_SESSION_SCOPE !== "global" || callerEnvironment.YUI_ROLE !== "operator") {
|
|
802
|
+
throw usageError("Operator submission requires a public caller or the current global Operator.");
|
|
803
|
+
}
|
|
804
|
+
requireManagedGlobalCaller(store, callerEnvironment);
|
|
805
|
+
}
|
|
794
806
|
const now = clock(options);
|
|
795
807
|
const effectiveIntent = normalizeSubmissionIntent(intent);
|
|
796
808
|
const result = store.transaction((tx) => {
|
|
@@ -3401,6 +3413,7 @@ function updateWork(args, store, options) {
|
|
|
3401
3413
|
: { taskMainSnapshot: options.directTaskMainSnapshot })
|
|
3402
3414
|
}, now);
|
|
3403
3415
|
tx.saveWorkItem(task.id, updated);
|
|
3416
|
+
freezeCandidateContextSnapshot(tx, updated.candidates.at(-1), now);
|
|
3404
3417
|
recordTaskEvent(tx, task.id, "work.updated", {
|
|
3405
3418
|
workItemId: updated.id,
|
|
3406
3419
|
status: updated.status,
|
|
@@ -3452,8 +3465,8 @@ export function requireWorkItemAssignee(item) {
|
|
|
3452
3465
|
return item.assignee;
|
|
3453
3466
|
}
|
|
3454
3467
|
function dispatchWork(args, store, options) {
|
|
3455
|
-
const usage = "Task work dispatch usage: yui task work dispatch <task>/<work> [--input <text>] [--lane-role <role> ...].";
|
|
3456
|
-
const parsed = parseMultiValueTail(args, new Set(["--input"]), new Set(["--lane-role"]), usage);
|
|
3468
|
+
const usage = "Task work dispatch usage: yui task work dispatch <task>/<work> [--input <text>] [--context-ref <store/refId@digest> ...] [--lane-role <role> ...].";
|
|
3469
|
+
const parsed = parseMultiValueTail(args, new Set(["--input"]), new Set(["--lane-role", "--context-ref"]), usage);
|
|
3457
3470
|
exactPositionals(parsed.positionals, 1, usage);
|
|
3458
3471
|
const requestedLaneRoles = parsed.multiOptions.get("--lane-role") ?? [];
|
|
3459
3472
|
const now = clock(options);
|
|
@@ -3461,6 +3474,7 @@ function dispatchWork(args, store, options) {
|
|
|
3461
3474
|
const item = requireWorkItem(tx, parsed.positionals[0], options);
|
|
3462
3475
|
const task = requireTask(tx, item.taskId);
|
|
3463
3476
|
taskActor(tx, options, task.id);
|
|
3477
|
+
const requiredResources = readDispatchContextRefs(tx, task.id, parsed.multiOptions.get("--context-ref") ?? [], options.environment);
|
|
3464
3478
|
if (task.status !== "active")
|
|
3465
3479
|
throw usageError(inactiveTaskMessage(task, "dispatch"));
|
|
3466
3480
|
assertTaskExecutionEnabled(task, "dispatching work");
|
|
@@ -3520,7 +3534,7 @@ function dispatchWork(args, store, options) {
|
|
|
3520
3534
|
purpose: "execution",
|
|
3521
3535
|
workItemId: item.id,
|
|
3522
3536
|
workspace
|
|
3523
|
-
}, now, "controller");
|
|
3537
|
+
}, now, "controller", undefined, undefined, requiredResources);
|
|
3524
3538
|
const run = createRun(runId, task.id, role.name, roleAgentSessionResumeMode(tx.getTaskRoleSessionSet(task.id, role.name), effective.agentId, effective), createRunInput({
|
|
3525
3539
|
source: { type: "yui", channel: "workitem-dispatch" },
|
|
3526
3540
|
directive: rawInput,
|
|
@@ -3552,7 +3566,8 @@ function dispatchWork(args, store, options) {
|
|
|
3552
3566
|
const assignmentContext = contextSnapshotRef(freezeWorkItemExecutionAssignmentContextSnapshot(tx, {
|
|
3553
3567
|
taskId: task.id,
|
|
3554
3568
|
workItemId: item.id,
|
|
3555
|
-
executionGroupId: groupId
|
|
3569
|
+
executionGroupId: groupId,
|
|
3570
|
+
requiredResources
|
|
3556
3571
|
}, now));
|
|
3557
3572
|
const plans = roles.map((role, index) => {
|
|
3558
3573
|
const laneId = `${groupId}-lane-${index + 1}`;
|
|
@@ -3657,6 +3672,45 @@ function dispatchWork(args, store, options) {
|
|
|
3657
3672
|
? `Direct WorkItem AgentRun queued as ${dispatch.runs[0].id}\n`
|
|
3658
3673
|
: `Dispatch queued for ${dispatch.runs.length} replicated Lanes\n`;
|
|
3659
3674
|
}
|
|
3675
|
+
/** Explicit, bounded reads; prose never grants access or selects records. */
|
|
3676
|
+
export function readDispatchContextRefs(store, taskId, selectors, environment = {}) {
|
|
3677
|
+
if (selectors.length > 16 || new Set(selectors).size !== selectors.length) {
|
|
3678
|
+
throw usageError("Dispatch requires at most 16 distinct --context-ref values.");
|
|
3679
|
+
}
|
|
3680
|
+
return selectors.flatMap(selector => {
|
|
3681
|
+
const match = /^([^/]+)\/(.+)@([0-9a-f]{64})$/u.exec(selector);
|
|
3682
|
+
if (match === null) {
|
|
3683
|
+
throw usageError(`Invalid required Context reference: ${selector}. Use store/refId@digest from task context list/inspect.`);
|
|
3684
|
+
}
|
|
3685
|
+
const [, family, refId, digest] = match;
|
|
3686
|
+
try {
|
|
3687
|
+
const entry = readTaskContextResource(store, taskId, {
|
|
3688
|
+
store: family, refId: refId, digest: digest
|
|
3689
|
+
}, environment);
|
|
3690
|
+
const resources = [{
|
|
3691
|
+
ref: { ...entry.ref, layer: "L4", evidenceOf: "dispatch-required" },
|
|
3692
|
+
value: entry.value
|
|
3693
|
+
}];
|
|
3694
|
+
// Selecting a result Message selects its already-authorized original
|
|
3695
|
+
// result too, not just a pointer the receiving Assignment cannot expand.
|
|
3696
|
+
if (family === "task-message" && "result" in entry) {
|
|
3697
|
+
const message = entry.value;
|
|
3698
|
+
const source = store.getRun(taskId, message.resultRef.runId);
|
|
3699
|
+
const value = sourceRunContextValue(source);
|
|
3700
|
+
resources.push({
|
|
3701
|
+
ref: { layer: "L4", store: "source-run", refId: source.id,
|
|
3702
|
+
revision: source.updatedAt, digest: contextContentDigest(value),
|
|
3703
|
+
evidenceOf: "dispatch-required", summary: `Original result for Message ${message.id}` },
|
|
3704
|
+
value
|
|
3705
|
+
});
|
|
3706
|
+
}
|
|
3707
|
+
return resources;
|
|
3708
|
+
}
|
|
3709
|
+
catch (error) {
|
|
3710
|
+
throw usageError(`Required Context reference ${selector} cannot be frozen: ${error instanceof Error ? error.message : String(error)} Read an authorized current reference, correct the selection or include the requirement in the WorkItem, then dispatch again.`);
|
|
3711
|
+
}
|
|
3712
|
+
});
|
|
3713
|
+
}
|
|
3660
3714
|
function replicatedProducerAssignmentInput(input, requiresCodeRef) {
|
|
3661
3715
|
return [
|
|
3662
3716
|
input,
|
|
@@ -5040,12 +5094,20 @@ function retryRunOperation(args, store, options) {
|
|
|
5040
5094
|
throw dataError(`AgentRun ${previous.id} direct WorkItem workspace is missing or has drifted.`);
|
|
5041
5095
|
}
|
|
5042
5096
|
}
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5097
|
+
// Direct Task delivery resumes the native Session's captured authority.
|
|
5098
|
+
// New source/Role defaults are adopted only after formal replacement.
|
|
5099
|
+
const directSession = retryItem === null && previous.purpose === "execution"
|
|
5100
|
+
&& retryManagedWorkspace?.owner.type === "task"
|
|
5101
|
+
? sessions?.sessions[role.activeAgentId] : undefined;
|
|
5102
|
+
const effective = retryLane?.effective
|
|
5103
|
+
?? (directSession === undefined ? undefined
|
|
5104
|
+
: effectiveLaunchWithTaskMainWorkspace(directSession.effective, retryManagedWorkspace))
|
|
5105
|
+
?? resolveEffectiveLaunch({
|
|
5106
|
+
role,
|
|
5107
|
+
purpose: previous.purpose,
|
|
5108
|
+
...(retryManagedWorkspace === undefined ? {} : { workspace: retryManagedWorkspace }),
|
|
5109
|
+
...(retryItem === null ? {} : { workItemWriteProjectIds: retryItem.writeProjectIds })
|
|
5110
|
+
});
|
|
5049
5111
|
const runId = tx.nextRunId(task.id);
|
|
5050
5112
|
const runningGroup = retryGroup === undefined || retryLane === undefined
|
|
5051
5113
|
? undefined
|
|
@@ -5071,13 +5133,19 @@ function retryRunOperation(args, store, options) {
|
|
|
5071
5133
|
const input = retriesSynthesisMain
|
|
5072
5134
|
? previous.inputs[0].input
|
|
5073
5135
|
: (() => {
|
|
5136
|
+
// A retry keeps the explicit frozen handoff, not today's mutable
|
|
5137
|
+
// versions of those records. Legacy Runs without a Snapshot retain
|
|
5138
|
+
// the existing explicit recovery from current facts.
|
|
5139
|
+
const requiredResources = previous.inputs[0].input.contextSnapshotRef === undefined
|
|
5140
|
+
? []
|
|
5141
|
+
: readRunContextSnapshot(tx, previous).resources.filter(entry => entry.ref.evidenceOf === "dispatch-required");
|
|
5074
5142
|
const retrySnapshot = freezeRunContextSnapshot(tx, {
|
|
5075
5143
|
taskId: task.id,
|
|
5076
5144
|
roleName: role.name,
|
|
5077
5145
|
purpose: previous.purpose,
|
|
5078
5146
|
...(retryManagedWorkspace === undefined ? {} : { workspace: retryManagedWorkspace }),
|
|
5079
5147
|
...(previous.workItemId === undefined ? {} : { workItemId: previous.workItemId })
|
|
5080
|
-
}, now, "controller", retryGroup?.assignment.contextSnapshotRef);
|
|
5148
|
+
}, now, "controller", retryGroup?.assignment.contextSnapshotRef, undefined, requiredResources, "frozen");
|
|
5081
5149
|
return createRunInput({
|
|
5082
5150
|
source: {
|
|
5083
5151
|
type: "yui",
|
|
@@ -5200,12 +5268,13 @@ function taskReviewProvenance(store, task, options, expected) {
|
|
|
5200
5268
|
break;
|
|
5201
5269
|
}
|
|
5202
5270
|
}
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5271
|
+
const target = store.getTaskWorkspace(task.id)?.entries.find(entry => entry.projectId === projectId)?.branch ?? committedAttempts[headIndex]?.targetRef;
|
|
5272
|
+
// A direct Task-main repair has no Integration at its new head. It is
|
|
5273
|
+
// still reviewable; conservatively exclude prior producers on this target
|
|
5274
|
+
// from reviewing it. This is an independence fence, not ancestry or proof
|
|
5275
|
+
// that any prior report validates the current head.
|
|
5276
|
+
const lineage = (headIndex < 0 ? committedAttempts : committedAttempts.slice(0, headIndex + 1))
|
|
5277
|
+
.filter(({ targetRef }) => targetRef === target);
|
|
5209
5278
|
for (const committed of lineage) {
|
|
5210
5279
|
if (committed.source.kind !== "work-item")
|
|
5211
5280
|
continue;
|
|
@@ -7,6 +7,7 @@ import { NodeGitWorkspace } from "../repository/gitWorkspace.js";
|
|
|
7
7
|
import { assertProjectActive, resolveProject } from "../repository/project.js";
|
|
8
8
|
import { FileTaskWorkspacePreparer } from "../repository/taskWorkspacePreparer.js";
|
|
9
9
|
import { assertTaskDeliveryAuthority as taskLocalActor } from "../task/taskAuthority.js";
|
|
10
|
+
import { taskIntegrationTargetCheckout } from "../task/taskAuthority.js";
|
|
10
11
|
import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
|
|
11
12
|
import { governingWorkItemCandidate } from "../workItem/workItem.js";
|
|
12
13
|
import { workspaceProjectEntry } from "../worktree/managedWorkspace.js";
|
|
@@ -132,6 +133,7 @@ async function start(args, store, home, now, options) {
|
|
|
132
133
|
if (targetRef === undefined) {
|
|
133
134
|
throw usageError(`Task main worktree is not ready; reconcile the Task first: ${task.id}.`);
|
|
134
135
|
}
|
|
136
|
+
taskIntegrationTargetCheckout(store, options.environment, task.id, project.id, targetRef);
|
|
135
137
|
const expectedHead = (await new NodeGitWorkspace().inspect(mainEntry.path, targetRef)).baseCommit;
|
|
136
138
|
const snapshotEntry = candidate.workspace.entries.find(({ projectId }) => projectId === project.id);
|
|
137
139
|
const resultCommit = candidate.gitSnapshot.projects.find(({ projectId }) => projectId === project.id)?.commit;
|
|
@@ -139,7 +141,7 @@ async function start(args, store, home, now, options) {
|
|
|
139
141
|
throw usageError(`WorkItem result Project snapshot is incomplete: ${workItem.id}/${project.id}.`);
|
|
140
142
|
}
|
|
141
143
|
const integration = store.transaction((tx) => {
|
|
142
|
-
|
|
144
|
+
taskIntegrationTargetCheckout(tx, options.environment, task.id, project.id, targetRef);
|
|
143
145
|
const created = createIntegrationAttempt({
|
|
144
146
|
id: tx.nextIntegrationAttemptId(task.id),
|
|
145
147
|
taskId: task.id,
|
|
@@ -6,7 +6,7 @@ import { createIntegrationAttempt } from "../integration/integrationAttempt.js";
|
|
|
6
6
|
import { NodeGitWorkspace } from "../repository/gitWorkspace.js";
|
|
7
7
|
import { FileTaskWorkspacePreparer } from "../repository/taskWorkspacePreparer.js";
|
|
8
8
|
import { workspaceProjectEntry } from "../worktree/managedWorkspace.js";
|
|
9
|
-
import { taskLocalActor } from "../task/taskAuthority.js";
|
|
9
|
+
import { taskLocalActor, assertTaskProjectWriteAuthority } from "../task/taskAuthority.js";
|
|
10
10
|
export async function runTaskUpstreamCommand(args, store, home, options = {}) {
|
|
11
11
|
const [command, ...rest] = args;
|
|
12
12
|
if (command === "integrate")
|
|
@@ -71,6 +71,7 @@ async function integrateUpstream(args, store, home, options) {
|
|
|
71
71
|
let phase = "prepare";
|
|
72
72
|
let integrationId;
|
|
73
73
|
try {
|
|
74
|
+
assertTaskProjectWriteAuthority(store, options.environment, task.id, binding.projectId);
|
|
74
75
|
const project = store.getProject(binding.projectId);
|
|
75
76
|
if (project === null)
|
|
76
77
|
throw usageError(`Project not found: ${binding.projectId}.`);
|
|
@@ -94,6 +95,7 @@ async function integrateUpstream(args, store, home, options) {
|
|
|
94
95
|
branch: binding.baseRef
|
|
95
96
|
});
|
|
96
97
|
const attempt = store.transaction((tx) => {
|
|
98
|
+
assertTaskProjectWriteAuthority(tx, options.environment, task.id, binding.projectId);
|
|
97
99
|
const created = createIntegrationAttempt({
|
|
98
100
|
id: tx.nextIntegrationAttemptId(task.id),
|
|
99
101
|
taskId: task.id,
|
|
@@ -10,7 +10,112 @@ import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
|
|
|
10
10
|
import { contextContentDigest, contextSnapshotRef, createContextSnapshot, validateContextSnapshot } from "./contextSnapshot.js";
|
|
11
11
|
import { MAX_SYNTHESIS_SOURCE_RUNS, sourceRunContextValue } from "./sourceRunContext.js";
|
|
12
12
|
export const RUN_CONTEXT_PACK_SCHEMA_VERSION = 1;
|
|
13
|
-
|
|
13
|
+
function mergeRequiredContext(automatic, required, requiredPolicy = "current") {
|
|
14
|
+
const entries = new Map(automatic.map(entry => [`${entry.ref.store}/${entry.ref.refId}`, entry]));
|
|
15
|
+
for (const entry of required) {
|
|
16
|
+
const key = `${entry.ref.store}/${entry.ref.refId}`;
|
|
17
|
+
const existing = entries.get(key);
|
|
18
|
+
if (requiredPolicy === "current" && existing !== undefined && existing.ref.digest !== entry.ref.digest) {
|
|
19
|
+
throw new Error(`Required Context changed during dispatch: ${key}. Read its current reference and dispatch again.`);
|
|
20
|
+
}
|
|
21
|
+
entries.set(key, entry);
|
|
22
|
+
}
|
|
23
|
+
return [...entries.values()];
|
|
24
|
+
}
|
|
25
|
+
/** Pin the original result together with the candidate, using the existing
|
|
26
|
+
* immutable Snapshot store rather than a second result/provenance ledger. */
|
|
27
|
+
export function freezeCandidateContextSnapshot(store, candidate, now) {
|
|
28
|
+
if (candidate.source.type !== "run")
|
|
29
|
+
return undefined;
|
|
30
|
+
const source = store.getRun(candidate.taskId, candidate.source.runId);
|
|
31
|
+
if (source === null || source.taskId !== candidate.taskId
|
|
32
|
+
|| source.workItemId !== candidate.workItemId || source.purpose !== "execution"
|
|
33
|
+
|| source.status !== "completed" || source.result === undefined
|
|
34
|
+
|| Date.parse(source.updatedAt) > Date.parse(candidate.createdAt)) {
|
|
35
|
+
throw new Error(`Candidate source report is missing or mismatched: ${candidate.workItemId}/${candidate.id}.`);
|
|
36
|
+
}
|
|
37
|
+
const evidenceOf = `${candidate.workItemId}/${candidate.id}`;
|
|
38
|
+
const report = materialize("L4", "source-run", source.id, sourceRunContextValue(source));
|
|
39
|
+
const resources = [
|
|
40
|
+
materialize("L3", "candidate", evidenceOf, candidate),
|
|
41
|
+
{ ...report, ref: { ...report.ref, evidenceOf } }
|
|
42
|
+
];
|
|
43
|
+
const snapshot = createContextSnapshot({
|
|
44
|
+
id: store.nextContextSnapshotId(candidate.taskId),
|
|
45
|
+
taskId: candidate.taskId, scope: "workitem", scopeRef: `candidate:${evidenceOf}`,
|
|
46
|
+
sequence: 1, refs: resources.map(({ ref }) => ref), resources, acceptRefs: [],
|
|
47
|
+
frozenAt: now, frozenBy: "controller"
|
|
48
|
+
});
|
|
49
|
+
store.saveContextSnapshot(snapshot);
|
|
50
|
+
return snapshot;
|
|
51
|
+
}
|
|
52
|
+
function candidateReviewContext(store, candidate) {
|
|
53
|
+
const identity = `${candidate.workItemId}/${candidate.id}`;
|
|
54
|
+
if (candidate.source.type === "direct") {
|
|
55
|
+
return [materialize("L3", "candidate", identity, candidate)];
|
|
56
|
+
}
|
|
57
|
+
const sourceRunId = candidate.source.runId;
|
|
58
|
+
const snapshots = store.listContextSnapshots(candidate.taskId).filter(snapshot => snapshot.scope === "workitem" && snapshot.scopeRef === `candidate:${identity}`);
|
|
59
|
+
if (snapshots.length !== 1) {
|
|
60
|
+
throw new Error(`Frozen candidate report is unavailable: ${identity}. Inspect source Run ${candidate.taskId}/${sourceRunId}; for a new Review use newly authorized delivery with a newly captured Candidate. Historical Snapshots are not rewritten.`);
|
|
61
|
+
}
|
|
62
|
+
const snapshot = validateContextSnapshot(snapshots[0]);
|
|
63
|
+
const proof = snapshot.resources.find(({ ref }) => ref.store === "candidate" && ref.refId === identity);
|
|
64
|
+
const report = snapshot.resources.find(({ ref }) => ref.store === "source-run"
|
|
65
|
+
&& ref.refId === sourceRunId && ref.evidenceOf === identity);
|
|
66
|
+
const source = store.getRun(candidate.taskId, sourceRunId);
|
|
67
|
+
if (proof?.ref.digest !== contextContentDigest(candidate) || report === undefined
|
|
68
|
+
|| source === null || source.status !== "completed" || source.result === undefined
|
|
69
|
+
|| source.taskId !== candidate.taskId || source.workItemId !== candidate.workItemId
|
|
70
|
+
|| source.purpose !== "execution"
|
|
71
|
+
|| report.ref.digest !== contextContentDigest(sourceRunContextValue(source))) {
|
|
72
|
+
throw new Error(`Frozen candidate source/report drifted: ${identity}. Inspect the exact source and capture a new Candidate; do not substitute a newer result.`);
|
|
73
|
+
}
|
|
74
|
+
return [proof, report];
|
|
75
|
+
}
|
|
76
|
+
/** Reuse only exact head/target evidence. Temporal ordering and a shared branch
|
|
77
|
+
* name do not prove ancestry; absent proof is a reviewable evidence gap. */
|
|
78
|
+
function taskReviewCandidateSources(store, taskId, heads) {
|
|
79
|
+
const selected = new Map();
|
|
80
|
+
const select = (candidate) => selected.set(`${candidate.workItemId}/${candidate.id}`, candidate);
|
|
81
|
+
const workspace = store.getTaskWorkspace(taskId);
|
|
82
|
+
const attempts = store.listIntegrationAttempts(taskId);
|
|
83
|
+
const items = store.listWorkItems(taskId);
|
|
84
|
+
const unmatchedHeads = [];
|
|
85
|
+
for (const { projectId, commit } of heads.projects) {
|
|
86
|
+
const targetRef = workspace?.entries.find(entry => entry.projectId === projectId)?.branch;
|
|
87
|
+
let matched = false;
|
|
88
|
+
for (const attempt of attempts.filter(entry => entry.projectId === projectId
|
|
89
|
+
&& entry.status === "committed" && entry.candidateCommit === commit
|
|
90
|
+
&& entry.targetRef === targetRef)) {
|
|
91
|
+
if (attempt.source.kind !== "work-item")
|
|
92
|
+
continue;
|
|
93
|
+
const source = attempt.source;
|
|
94
|
+
const item = store.getWorkItem(taskId, source.workItemId);
|
|
95
|
+
const candidate = [...(item?.candidates ?? [])].reverse().find(entry => entry.gitSnapshot?.projects.some(project => project.projectId === projectId && project.commit === source.resultCommit));
|
|
96
|
+
if (candidate !== undefined) {
|
|
97
|
+
select(candidate);
|
|
98
|
+
matched = true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (const item of items) {
|
|
102
|
+
const candidate = [...item.candidates].reverse().find(entry => entry.taskMainSnapshot?.projects.some(project => project.projectId === projectId && project.headCommit === commit
|
|
103
|
+
&& project.branch === targetRef)
|
|
104
|
+
|| entry.gitSnapshot?.projects.some(project => project.projectId === projectId && project.commit === commit));
|
|
105
|
+
if (candidate !== undefined) {
|
|
106
|
+
select(candidate);
|
|
107
|
+
matched = true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (!matched)
|
|
111
|
+
unmatchedHeads.push(`${projectId}@${commit}`);
|
|
112
|
+
}
|
|
113
|
+
return { candidates: [...selected.values()], unmatchedHeads };
|
|
114
|
+
}
|
|
115
|
+
export function freezeRunContextSnapshot(store, run, now, frozenBy = "controller", baselineRef, sourceRunIds, requiredResources = [],
|
|
116
|
+
// Only retry of a validated prior Snapshot inherits frozen values over live
|
|
117
|
+
// automatic context; a new dispatch must still match current references.
|
|
118
|
+
requiredPolicy = "current") {
|
|
14
119
|
if (baselineRef !== undefined) {
|
|
15
120
|
const baseline = store.getContextSnapshot(run.taskId, baselineRef.id);
|
|
16
121
|
if (baseline === null
|
|
@@ -28,7 +133,7 @@ export function freezeRunContextSnapshot(store, run, now, frozenBy = "controller
|
|
|
28
133
|
...collectRunContextOverlays(store, run),
|
|
29
134
|
...collectSourceRunContext(store, run, sourceRunIds)
|
|
30
135
|
];
|
|
31
|
-
const resources = [...new Map([...baseline.resources, ...overlays].map((entry) => [
|
|
136
|
+
const resources = [...new Map(mergeRequiredContext([...baseline.resources, ...overlays], requiredResources, requiredPolicy).map((entry) => [
|
|
32
137
|
contextRefIdentity(entry.ref),
|
|
33
138
|
entry
|
|
34
139
|
])).values()].sort((left, right) => (contextRefIdentity(left.ref).localeCompare(contextRefIdentity(right.ref))));
|
|
@@ -60,7 +165,7 @@ export function freezeRunContextSnapshot(store, run, now, frozenBy = "controller
|
|
|
60
165
|
? "workitem"
|
|
61
166
|
: "task";
|
|
62
167
|
const scopeRef = run.reviewRoundId ?? run.workItemId;
|
|
63
|
-
const materialized = collectAuthorizedContext(store, run);
|
|
168
|
+
const materialized = mergeRequiredContext(collectAuthorizedContext(store, run), requiredResources, requiredPolicy);
|
|
64
169
|
const previous = store.listContextSnapshots(run.taskId)
|
|
65
170
|
.filter((candidate) => candidate.scope === scope && candidate.scopeRef === scopeRef)
|
|
66
171
|
.sort((left, right) => left.sequence - right.sequence)
|
|
@@ -117,7 +222,7 @@ export function freezeWorkItemExecutionAssignmentContextSnapshot(store, input, n
|
|
|
117
222
|
materialized.push(materialize("L1", "project-knowledge", `${project.id}:${entry.id}`, { projectId: project.id, ...entry }));
|
|
118
223
|
}
|
|
119
224
|
}
|
|
120
|
-
const resources = [...new Map(materialized.map((entry) => [
|
|
225
|
+
const resources = [...new Map(mergeRequiredContext(materialized, input.requiredResources ?? []).map((entry) => [
|
|
121
226
|
contextRefIdentity(entry.ref),
|
|
122
227
|
entry
|
|
123
228
|
])).values()].sort((left, right) => (contextRefIdentity(left.ref).localeCompare(contextRefIdentity(right.ref))));
|
|
@@ -365,7 +470,14 @@ function collectAuthorizedContext(store, run) {
|
|
|
365
470
|
const round = store.getReviewRound(task.id, run.reviewRoundId);
|
|
366
471
|
if (round === null)
|
|
367
472
|
throw new Error(`AgentRun ReviewRound not found: ${run.reviewRoundId}.`);
|
|
368
|
-
|
|
473
|
+
let roundContext = materialize("L3", "review-round", round.id, round);
|
|
474
|
+
if (round.scope === "work-item") {
|
|
475
|
+
const item = store.getWorkItem(task.id, round.workItemId);
|
|
476
|
+
const candidate = item?.candidates.find(({ id }) => id === round.candidateId);
|
|
477
|
+
if (candidate === undefined)
|
|
478
|
+
throw new Error(`Review candidate is unavailable: ${round.candidateId}.`);
|
|
479
|
+
result.push(...candidateReviewContext(store, candidate));
|
|
480
|
+
}
|
|
369
481
|
if (round.scope === "task") {
|
|
370
482
|
for (const item of store.listWorkItems(task.id).filter(({ status }) => status !== "retired")) {
|
|
371
483
|
const candidate = governingWorkItemCandidate(item);
|
|
@@ -374,7 +486,22 @@ function collectAuthorizedContext(store, run) {
|
|
|
374
486
|
result.push(materialize("L3", "candidate", `${item.id}/${candidate.id}`, candidate));
|
|
375
487
|
result.push(...candidateArtifacts(task.id, [candidate]));
|
|
376
488
|
}
|
|
489
|
+
const evidence = taskReviewCandidateSources(store, task.id, round.taskCandidate);
|
|
490
|
+
// Existing pointer metadata exposes the gap without changing the Round,
|
|
491
|
+
// inventing validation, or adding another persisted evidence record.
|
|
492
|
+
roundContext = {
|
|
493
|
+
...roundContext, ref: {
|
|
494
|
+
...roundContext.ref,
|
|
495
|
+
summary: `Task-final Review ${round.id}. Producer reports are evidence only for their named Candidate, not validation of the entire Task head.${evidence.unmatchedHeads.length === 0 ? ""
|
|
496
|
+
: ` Producer evidence gap: ${evidence.unmatchedHeads.join(", ")}. Review these fixed heads and report missing validation; the gap does not prohibit review.`}`
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
for (const candidate of evidence.candidates) {
|
|
500
|
+
result.push(...candidateReviewContext(store, candidate));
|
|
501
|
+
result.push(...candidateArtifacts(task.id, [candidate]));
|
|
502
|
+
}
|
|
377
503
|
}
|
|
504
|
+
result.push(roundContext);
|
|
378
505
|
if (round.deltaRecheck !== undefined) {
|
|
379
506
|
const previous = store.getReviewRound(task.id, round.deltaRecheck.previousReviewRoundId);
|
|
380
507
|
const previousRun = previous?.reviewerRunId === undefined
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { MAX_RUN_RESULT_OUTPUT_BYTES } from "../domain/agentResultTransport.js";
|
|
2
2
|
export const MAX_SYNTHESIS_SOURCE_RUNS = 8;
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
// A Task review may combine several candidate reports, synthesis and
|
|
4
|
+
// continuation evidence. Preserve the existing aggregate byte ceiling.
|
|
5
|
+
export const MAX_CONTEXT_SOURCE_RUNS = 64;
|
|
6
|
+
export const MAX_CONTEXT_SOURCE_RUN_BYTES = (MAX_SYNTHESIS_SOURCE_RUNS + 1) * (MAX_RUN_RESULT_OUTPUT_BYTES + 16 * 1024);
|
|
5
7
|
/**
|
|
6
8
|
* Frozen input for synthesis. The main Agent needs the producer identity and
|
|
7
9
|
* exact result, not another copy of its prompt history, launch configuration,
|
|
@@ -3,6 +3,7 @@ import { isGitArtifactRefString, parseGitArtifactRef } from "../artifacts/gitArt
|
|
|
3
3
|
import { taskNotFound, usageError } from "../errors/cliError.js";
|
|
4
4
|
import { expandTaskMessageResult } from "../message/message.js";
|
|
5
5
|
import { resolveManagedTaskReader } from "../runtime/managedCaller.js";
|
|
6
|
+
import { hasManagedIdentity } from "../runtime/managedIdentity.js";
|
|
6
7
|
import { providerRetryProjection } from "../runtime/providerRetry.js";
|
|
7
8
|
import { projectTaskRemoteDeliveryFromStore } from "../task/remoteDeliveryService.js";
|
|
8
9
|
import { retiredTaskRecordIds, TASK_RECORD_RETIRED_EVENT } from "../task/taskRecordRetirement.js";
|
|
@@ -64,6 +65,10 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
64
65
|
store, total, returned: records.filter(record => record.ref.store === store).length
|
|
65
66
|
})),
|
|
66
67
|
discovery: `task context list ${taskId} --store <store>`,
|
|
68
|
+
commandDiscovery: {
|
|
69
|
+
help: "yui help", describe: "yui --json help",
|
|
70
|
+
note: "Help and completion share the current caller's offline command view. Follow that view for operation-specific discovery; execution always revalidates authority."
|
|
71
|
+
},
|
|
67
72
|
detail: `task context inspect ${taskId} --store <store> --ref <refId> --digest <digest>`,
|
|
68
73
|
attention,
|
|
69
74
|
records, count,
|
|
@@ -153,6 +158,11 @@ export function readTaskContextDelta(store, taskId, input, environment = {}) {
|
|
|
153
158
|
});
|
|
154
159
|
}
|
|
155
160
|
export function inspectTaskContext(store, taskId, selector, environment = {}) {
|
|
161
|
+
const response = readTaskContextResource(store, taskId, selector, environment);
|
|
162
|
+
return boundedDocument(response, `task:${taskId}/${selector.store}/${selector.refId}`, selector.cursor);
|
|
163
|
+
}
|
|
164
|
+
/** The same authorized read as inspect, before transport pagination. */
|
|
165
|
+
export function readTaskContextResource(store, taskId, selector, environment = {}) {
|
|
156
166
|
return store.readTransaction((reader) => {
|
|
157
167
|
const scope = authorizeContext(reader, taskId, environment);
|
|
158
168
|
const value = isAllowed(scope.allow, selector.store, selector.refId)
|
|
@@ -172,7 +182,7 @@ export function inspectTaskContext(store, taskId, selector, environment = {}) {
|
|
|
172
182
|
? { result: expansion.result } : {}),
|
|
173
183
|
...(selector.store === "run" ? { execution: runExecutionObservation(value, reader.getTaskRoleSessionSet(taskId, value.roleName)?.providerBinding, reader.listEventsByType(taskId, ["runtime.agent-error"])) } : {}),
|
|
174
184
|
};
|
|
175
|
-
return
|
|
185
|
+
return response;
|
|
176
186
|
});
|
|
177
187
|
}
|
|
178
188
|
/** Discover one authorized family without copying its bodies or unrelated Task
|
|
@@ -269,8 +279,7 @@ export function resolveContextReader(store, environment) {
|
|
|
269
279
|
throw usageError("Only Operator may read Task context from a global Session.");
|
|
270
280
|
}
|
|
271
281
|
if (caller === undefined && environment.YUI_SESSION_SCOPE === undefined
|
|
272
|
-
&& (environment
|
|
273
|
-
|| environment.YUI_TASK_ID !== undefined || environment.YUI_AGENT_ID !== undefined)) {
|
|
282
|
+
&& hasManagedIdentity(environment)) {
|
|
274
283
|
throw usageError("Incomplete managed Context caller identity.");
|
|
275
284
|
}
|
|
276
285
|
if (environment.YUI_SESSION_SCOPE !== undefined && !["task", "global"].includes(environment.YUI_SESSION_SCOPE)) {
|