@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
|
@@ -3,7 +3,7 @@ import { projectAgentCapabilityConfig } from "./agentCapabilityConfig.js";
|
|
|
3
3
|
import { activeRoleAgentBinding } from "../role/role.js";
|
|
4
4
|
import { configuredAgentFingerprint, readAgentFailureContext } from "../runtime/agentFailureContext.js";
|
|
5
5
|
import { resolveManagedTaskReader } from "../runtime/managedCaller.js";
|
|
6
|
-
import {
|
|
6
|
+
import { assertTaskReadable } from "../context/taskContext.js";
|
|
7
7
|
/** Resolve authority and scope before any native metadata process is started. */
|
|
8
8
|
export function resolveTaskAgentCapabilities(query, store, environment = {}) {
|
|
9
9
|
const { taskId, roleName, errorId } = query;
|
|
@@ -19,7 +19,7 @@ export function resolveTaskAgentCapabilities(query, store, environment = {}) {
|
|
|
19
19
|
let expectedFingerprint;
|
|
20
20
|
let expectedComponent;
|
|
21
21
|
if (errorId !== undefined) {
|
|
22
|
-
|
|
22
|
+
assertTaskReadable(store, taskId, environment);
|
|
23
23
|
const event = store.listEventsByType(taskId, ["runtime.agent-error"]).find(event => event.id === errorId);
|
|
24
24
|
if (event === undefined || event.payload.roleName !== roleName) {
|
|
25
25
|
throw usageError("Agent failure is not owned by this Task/Role.");
|
|
@@ -54,6 +54,7 @@ export function renderRoleDetails(title, role, input) {
|
|
|
54
54
|
...(!isEnabled(role) ? [` Availability ${roleDisabledReason(role)}`] : []),
|
|
55
55
|
` Active Agent ${role.activeAgentId}`,
|
|
56
56
|
` Workspace ${role.workspace}`,
|
|
57
|
+
` Instance code ${role.instanceCode ?? "-"} (display only; next Session launch)`,
|
|
57
58
|
...("taskId" in role ? [
|
|
58
59
|
` Desired environment ${role.executionEnvironment === undefined
|
|
59
60
|
? "managed workspace"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function validateInstanceCode(value) {
|
|
2
|
+
if (typeof value !== "string" || value.length === 0 || value.length > 24
|
|
3
|
+
|| !/^[\p{L}\p{N}\p{M}_-]+$/u.test(value)) {
|
|
4
|
+
throw new Error("Instance code must be 1–24 UTF-16 units of Unicode letters, numbers, marks, '_' or '-'; omit '@'.");
|
|
5
|
+
}
|
|
6
|
+
return value;
|
|
7
|
+
}
|
package/dist/role/role.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { validateInstanceCode } from "./instanceCode.js";
|
|
2
3
|
import { assertRoleEnabled, validateEnabled } from "./availability.js";
|
|
3
4
|
import { requireKnownFields } from "../domain/validation.js";
|
|
4
5
|
import { jobScopeKey } from "../job/jobDefinition.js";
|
|
@@ -49,6 +50,7 @@ export function copyGlobalRoleToTaskRole(globalRole, taskId, now, name = globalR
|
|
|
49
50
|
const timestamp = now.toISOString();
|
|
50
51
|
return validateTaskRole({
|
|
51
52
|
...cloneProfile(globalRole),
|
|
53
|
+
...(globalRole.instanceCode === undefined ? {} : { instanceCode: globalRole.instanceCode }),
|
|
52
54
|
schemaVersion: 1,
|
|
53
55
|
enabled: true,
|
|
54
56
|
launchRevision: 1,
|
|
@@ -75,6 +77,8 @@ export function updateGlobalRole(role, patch, now) {
|
|
|
75
77
|
updatedAt: now.toISOString()
|
|
76
78
|
};
|
|
77
79
|
clearProfileFields(updated, patch);
|
|
80
|
+
if (Object.hasOwn(patch, "instanceCode") && patch.instanceCode === undefined)
|
|
81
|
+
delete updated.instanceCode;
|
|
78
82
|
updated.launchRevision = isDeepStrictEqual(desiredBefore, desiredLaunchProjection(updated))
|
|
79
83
|
? role.launchRevision
|
|
80
84
|
: role.launchRevision + 1;
|
|
@@ -94,6 +98,8 @@ export function updateRole(role, patch, now) {
|
|
|
94
98
|
updated.executionEnvironment = cloneJson(patch.executionEnvironment);
|
|
95
99
|
}
|
|
96
100
|
clearProfileFields(updated, patch);
|
|
101
|
+
if (Object.hasOwn(patch, "instanceCode") && patch.instanceCode === undefined)
|
|
102
|
+
delete updated.instanceCode;
|
|
97
103
|
updated.launchRevision = isDeepStrictEqual(desiredBefore, desiredLaunchProjection(updated))
|
|
98
104
|
? role.launchRevision
|
|
99
105
|
: role.launchRevision + 1;
|
|
@@ -222,6 +228,7 @@ function createRoleOwner(name, bindings, activeAgentId, workspace, now, profile,
|
|
|
222
228
|
const timestamp = now.toISOString();
|
|
223
229
|
return {
|
|
224
230
|
...cloneProfile(profile),
|
|
231
|
+
...(profile.instanceCode === undefined ? {} : { instanceCode: validateInstanceCode(profile.instanceCode) }),
|
|
225
232
|
enabled: true,
|
|
226
233
|
launchRevision: 1,
|
|
227
234
|
defaultAccess,
|
|
@@ -234,6 +241,8 @@ function createRoleOwner(name, bindings, activeAgentId, workspace, now, profile,
|
|
|
234
241
|
};
|
|
235
242
|
}
|
|
236
243
|
function validateRoleOwner(role) {
|
|
244
|
+
if (role.instanceCode !== undefined)
|
|
245
|
+
validateInstanceCode(role.instanceCode);
|
|
237
246
|
validateEnabled(role.enabled);
|
|
238
247
|
if (role.schemaVersion !== 1) {
|
|
239
248
|
throw new Error("Role schema version is invalid.");
|
|
@@ -303,6 +312,7 @@ function assertSessionOwnerMatchesRole(role, sessions) {
|
|
|
303
312
|
}
|
|
304
313
|
function cloneRolePatch(patch) {
|
|
305
314
|
return {
|
|
315
|
+
...(patch.instanceCode === undefined ? {} : { instanceCode: validateInstanceCode(patch.instanceCode) }),
|
|
306
316
|
...(patch.enabled === undefined ? {} : { enabled: patch.enabled }),
|
|
307
317
|
...(patch.name === undefined ? {} : { name: requireSafeIdentity(patch.name, "Role name") }),
|
|
308
318
|
...(patch.activeAgentId === undefined
|
|
@@ -81,6 +81,7 @@ class BuiltinAgentEndpoint {
|
|
|
81
81
|
driver;
|
|
82
82
|
configuration;
|
|
83
83
|
get nativeAccess() { return this.driver.nativeAccess; }
|
|
84
|
+
get appliedGeneratedTitle() { return this.driver.appliedGeneratedTitle; }
|
|
84
85
|
get ownedProcessId() { return this.driver.ownedProcessId; }
|
|
85
86
|
get nativeAccountHome() { return this.driver.nativeAccountHome; }
|
|
86
87
|
capabilities;
|
|
@@ -120,7 +120,13 @@ export async function runAgentHost(input) {
|
|
|
120
120
|
const recentSteerAttempts = new Map();
|
|
121
121
|
await replayExitOutbox(input.home);
|
|
122
122
|
const updateSnapshot = (next) => {
|
|
123
|
-
|
|
123
|
+
const { appliedGeneratedTitle: _oldTitle, ...rest } = next;
|
|
124
|
+
snapshot = validateSnapshot({
|
|
125
|
+
...rest,
|
|
126
|
+
...(session?.appliedGeneratedTitle === undefined ? {} : {
|
|
127
|
+
appliedGeneratedTitle: session.appliedGeneratedTitle
|
|
128
|
+
})
|
|
129
|
+
});
|
|
124
130
|
};
|
|
125
131
|
const authorityFields = () => ({
|
|
126
132
|
...(session === undefined ? {} : { endpointImplementation: session.configuration.implementation }),
|
|
@@ -374,6 +380,8 @@ export async function runAgentHost(input) {
|
|
|
374
380
|
&& currentPayload.environment.YUI_ROLE === "operator")
|
|
375
381
|
? {}
|
|
376
382
|
: { sessionTitle: previousControl.sessionTitle }),
|
|
383
|
+
...(previousControl.previousSessionTitle === undefined
|
|
384
|
+
? {} : { previousSessionTitle: previousControl.previousSessionTitle }),
|
|
377
385
|
codexThread: disconnectedSession.configuration.threadOptions,
|
|
378
386
|
endpointImplementation: disconnectedSession.configuration.implementation,
|
|
379
387
|
...(ownedTurn === undefined ? {} : { ownedTurn }),
|
|
@@ -167,6 +167,9 @@ function validateProviderControl(control) {
|
|
|
167
167
|
throw new Error("Agent Host Provider session title is invalid.");
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
|
+
if (control.previousSessionTitle !== undefined) {
|
|
171
|
+
text(control.previousSessionTitle, "previousSessionTitle");
|
|
172
|
+
}
|
|
170
173
|
validateProviderAuthorityFence(control.authority);
|
|
171
174
|
}
|
|
172
175
|
function validateCodexThreadOptions(options) {
|
|
@@ -15,6 +15,9 @@ export function createRuntimeBinding(input) {
|
|
|
15
15
|
...(input.nativeSessionId === undefined
|
|
16
16
|
? {}
|
|
17
17
|
: { nativeSessionId: requireText(input.nativeSessionId, "Native session id") }),
|
|
18
|
+
...(input.appliedGeneratedTitle === undefined ? {} : {
|
|
19
|
+
appliedGeneratedTitle: requireText(input.appliedGeneratedTitle, "Applied generated title")
|
|
20
|
+
}),
|
|
18
21
|
...(input.providerAuthority === undefined
|
|
19
22
|
? {}
|
|
20
23
|
: { providerAuthority: validateProviderAuthorityFence(input.providerAuthority) })
|
|
@@ -1,29 +1,33 @@
|
|
|
1
1
|
import { resolveTimeZone } from "../config/timeZone.js";
|
|
2
|
+
import { validateInstanceCode } from "../role/instanceCode.js";
|
|
2
3
|
const TITLE_SEPARATOR = " · ";
|
|
3
4
|
export const MAX_SESSION_TITLE_LENGTH = 80;
|
|
4
5
|
const TASK_TITLE_MAX_LENGTH = 20;
|
|
5
|
-
export function taskRoleSessionTitle(task, roleName) {
|
|
6
|
-
const prefix = `Yui ${roleLabel(roleName)} ${normalizeSegment(task.id)}
|
|
6
|
+
export function taskRoleSessionTitle(task, roleName, options = {}) {
|
|
7
|
+
const prefix = `Yui ${roleLabel(roleName)} ${normalizeSegment(task.id)}`
|
|
8
|
+
+ (roleName === "leader" && options.executionAuthority === "planning" ? " Draft" : "")
|
|
9
|
+
+ instanceSegment(options.instanceCode);
|
|
7
10
|
const title = displayTitle(normalizeSegment(task.title), TASK_TITLE_MAX_LENGTH);
|
|
8
11
|
const full = `${prefix}${TITLE_SEPARATOR}${title}`;
|
|
9
12
|
if (full.length <= MAX_SESSION_TITLE_LENGTH)
|
|
10
13
|
return full;
|
|
11
14
|
if (prefix.length + TITLE_SEPARATOR.length + 1 > MAX_SESSION_TITLE_LENGTH) {
|
|
12
|
-
|
|
15
|
+
throw new Error("Session title identity exceeds the maximum length; shorten the Role name or instance code.");
|
|
13
16
|
}
|
|
14
|
-
const titleLength = MAX_SESSION_TITLE_LENGTH - prefix.length - TITLE_SEPARATOR.length
|
|
17
|
+
const titleLength = MAX_SESSION_TITLE_LENGTH - prefix.length - TITLE_SEPARATOR.length;
|
|
15
18
|
return `${prefix}${TITLE_SEPARATOR}${displayTitle(title, Math.max(titleLength, 1))}`;
|
|
16
19
|
}
|
|
17
|
-
export function resolveTaskRoleSessionTitle(existingTitle, task, roleName) {
|
|
20
|
+
export function resolveTaskRoleSessionTitle(existingTitle, task, roleName, options = {}) {
|
|
18
21
|
if (existingTitle !== undefined
|
|
19
22
|
&& existingTitle.length > 0
|
|
20
23
|
&& existingTitle.length <= MAX_SESSION_TITLE_LENGTH
|
|
21
|
-
&& !/[\r\n\0]/u.test(existingTitle)
|
|
24
|
+
&& !/[\r\n\0]/u.test(existingTitle)
|
|
25
|
+
&& existingTitle !== options.generatedTitle) {
|
|
22
26
|
return existingTitle;
|
|
23
27
|
}
|
|
24
|
-
return taskRoleSessionTitle(task, roleName);
|
|
28
|
+
return taskRoleSessionTitle(task, roleName, options);
|
|
25
29
|
}
|
|
26
|
-
export function operatorSessionTitle(now, configuredTimeZone) {
|
|
30
|
+
export function operatorSessionTitle(now, configuredTimeZone, instanceCode) {
|
|
27
31
|
if (!(now instanceof Date) || !Number.isFinite(now.getTime())) {
|
|
28
32
|
throw new TypeError("Operator session title date is invalid.");
|
|
29
33
|
}
|
|
@@ -35,7 +39,23 @@ export function operatorSessionTitle(now, configuredTimeZone) {
|
|
|
35
39
|
if (parts.month === undefined || parts.day === undefined) {
|
|
36
40
|
throw new TypeError("Operator session title date is unavailable.");
|
|
37
41
|
}
|
|
38
|
-
|
|
42
|
+
const prefix = instanceCode === undefined
|
|
43
|
+
? `Yui${TITLE_SEPARATOR}Operator`
|
|
44
|
+
: `Yui Operator${instanceSegment(instanceCode)}`;
|
|
45
|
+
return `${prefix}${TITLE_SEPARATOR}${parts.month}${parts.day}`;
|
|
46
|
+
}
|
|
47
|
+
export function globalRoleSessionTitle(roleName, now, configuredTimeZone, instanceCode) {
|
|
48
|
+
if (roleName === "operator")
|
|
49
|
+
return operatorSessionTitle(now, configuredTimeZone, instanceCode);
|
|
50
|
+
if (instanceCode === undefined)
|
|
51
|
+
return undefined;
|
|
52
|
+
const title = `Yui ${roleLabel(roleName)}${instanceSegment(instanceCode)}`;
|
|
53
|
+
if (title.length > MAX_SESSION_TITLE_LENGTH)
|
|
54
|
+
throw new Error("Session title identity exceeds the maximum length.");
|
|
55
|
+
return title;
|
|
56
|
+
}
|
|
57
|
+
function instanceSegment(value) {
|
|
58
|
+
return value === undefined ? "" : ` @${validateInstanceCode(value)}`;
|
|
39
59
|
}
|
|
40
60
|
function displayTitle(value, maxLength) {
|
|
41
61
|
if (value.length <= maxLength)
|
|
@@ -384,6 +384,7 @@ class CodexStructuredProviderSession {
|
|
|
384
384
|
onActivity;
|
|
385
385
|
mirror;
|
|
386
386
|
nativeAccountHome;
|
|
387
|
+
appliedGeneratedTitle;
|
|
387
388
|
nativeAccess;
|
|
388
389
|
#turnAttempts = new Map();
|
|
389
390
|
adapterId = "codex";
|
|
@@ -445,12 +446,20 @@ class CodexStructuredProviderSession {
|
|
|
445
446
|
throw error;
|
|
446
447
|
}
|
|
447
448
|
}
|
|
448
|
-
|
|
449
|
+
const threadMetadata = nativeConfiguration.thread;
|
|
450
|
+
const nativeTitle = typeof threadMetadata === "object" && threadMetadata !== null
|
|
451
|
+
&& !Array.isArray(threadMetadata) ? threadMetadata.name : undefined;
|
|
452
|
+
// A native rename belongs to the user. Missing metadata is not evidence
|
|
453
|
+
// that an old title is still ours; leave it alone on resume.
|
|
454
|
+
let appliedGeneratedTitle;
|
|
455
|
+
if (control.sessionTitle !== undefined && (control.mode === "new"
|
|
456
|
+
|| (control.previousSessionTitle !== undefined && nativeTitle === control.previousSessionTitle))) {
|
|
449
457
|
try {
|
|
450
458
|
await runtime.setConversationName({
|
|
451
459
|
conversationId,
|
|
452
460
|
name: control.sessionTitle
|
|
453
461
|
});
|
|
462
|
+
appliedGeneratedTitle = control.sessionTitle;
|
|
454
463
|
}
|
|
455
464
|
catch (error) {
|
|
456
465
|
onDiagnostic?.({
|
|
@@ -465,6 +474,7 @@ class CodexStructuredProviderSession {
|
|
|
465
474
|
}
|
|
466
475
|
const session = new CodexStructuredProviderSession(child, exit, processInstanceId, conversationId, runtime, onStarted, onTerminal, onGoal, onInput, onActivity, mirror, optionalId(initialized.codexHome));
|
|
467
476
|
session.nativeAccess = createCodexNativeAccess(conversationId, initialized, channel, openingMessages, nativeConfiguration);
|
|
477
|
+
session.appliedGeneratedTitle = appliedGeneratedTitle;
|
|
468
478
|
session.#activeTurnId = resumedActiveTurnId;
|
|
469
479
|
const ownedTurn = control.kind === "restore" ? control.ownedTurn : undefined;
|
|
470
480
|
session.#clientOwnedTurnId = ownedTurn?.turnId;
|
|
@@ -246,6 +246,7 @@ export class TmuxSessionHost {
|
|
|
246
246
|
agentId: request.agentId,
|
|
247
247
|
adapterId: request.adapterId,
|
|
248
248
|
effective: request.effective,
|
|
249
|
+
...(planned.generatedTitle === undefined ? {} : { generatedTitle: planned.generatedTitle }),
|
|
249
250
|
...(planned.launch.env.YUI_SESSION_TITLE === undefined
|
|
250
251
|
? {}
|
|
251
252
|
: { sessionTitle: planned.launch.env.YUI_SESSION_TITLE }),
|
|
@@ -436,6 +437,9 @@ export class TmuxSessionHost {
|
|
|
436
437
|
roleName: request.owner.roleName
|
|
437
438
|
}),
|
|
438
439
|
hostCreated,
|
|
440
|
+
...(providerSnapshot?.appliedGeneratedTitle === undefined ? {} : {
|
|
441
|
+
appliedGeneratedTitle: providerSnapshot.appliedGeneratedTitle
|
|
442
|
+
}),
|
|
439
443
|
...((providerSnapshot?.nativeSessionId ?? nativeSessionId) === undefined
|
|
440
444
|
? {}
|
|
441
445
|
: { nativeSessionId: providerSnapshot?.nativeSessionId ?? nativeSessionId }),
|
|
@@ -276,6 +276,16 @@ const MINOR_UPGRADES = Object.freeze([{
|
|
|
276
276
|
// No payload rewriting or historical revision invention. A newly opened
|
|
277
277
|
// reader starts empty; zero is the initial token until the first mutation.
|
|
278
278
|
sql: TASK_READ_REVISION_SCHEMA_SQL
|
|
279
|
+
}, {
|
|
280
|
+
fromVersion: "1.24",
|
|
281
|
+
toVersion: "1.25",
|
|
282
|
+
name: "role-instance-code-session-titles",
|
|
283
|
+
introducedIn: "next",
|
|
284
|
+
sourceChecksum: CURRENT_SCHEMA_CHECKSUM,
|
|
285
|
+
targetChecksum: CURRENT_SCHEMA_CHECKSUM,
|
|
286
|
+
// Optional Role presentation and generated-title provenance. Never infer
|
|
287
|
+
// ownership of historical titles, rewrite frozen profiles, or fill defaults.
|
|
288
|
+
sql: ""
|
|
279
289
|
}]);
|
|
280
290
|
export function storageMinorUpgradePlan(from) {
|
|
281
291
|
if (from === CURRENT_STORAGE_VERSION)
|
|
@@ -146,10 +146,14 @@ export function projectActor(environment) {
|
|
|
146
146
|
}
|
|
147
147
|
return "user";
|
|
148
148
|
}
|
|
149
|
+
/** Read-only Job requests carry the original identity, not the record's owner.
|
|
150
|
+
* The Controller authenticates it; mutations keep resolveJobCaller's target check. */
|
|
151
|
+
export function resolveJobReadCaller(environment, taskId) {
|
|
152
|
+
return resolveJobCaller(environment, environment?.YUI_SESSION_SCOPE === "task" ? environment.YUI_TASK_ID ?? "" : taskId);
|
|
153
|
+
}
|
|
149
154
|
/**
|
|
150
|
-
* Resolve
|
|
151
|
-
*
|
|
152
|
-
* caller cannot control another Task; an incomplete identity is not a user.
|
|
155
|
+
* Resolve execution identity. The Controller verifies it against durable state;
|
|
156
|
+
* a Task caller cannot control another Task or infer user authority.
|
|
153
157
|
*/
|
|
154
158
|
export function resolveJobCaller(environment, taskId) {
|
|
155
159
|
const env = environment ?? {};
|
|
@@ -88,6 +88,23 @@ use family discovery when a broader requirement/history audit is needed.
|
|
|
88
88
|
decision/milestone/event, Role show/status/Session inspection, wake detail,
|
|
89
89
|
InputRequest, Run context and frozen expansion use bounded detail reads.
|
|
90
90
|
|
|
91
|
+
Ordinary Task reads authenticate the original caller but do not restrict data
|
|
92
|
+
by its Task or Assignment ID. This single-user local product relies on local
|
|
93
|
+
filesystem permissions and authenticated service transports, not Task ownership
|
|
94
|
+
as a tenant boundary. Foreign current context identifies the caller's own Task
|
|
95
|
+
and labels its authority `reference-only`. Search, records, artifacts and native
|
|
96
|
+
conversation evidence do not switch identity, adopt another Run's instructions,
|
|
97
|
+
or grant writes, inbox handling, dispatch or external effects. Explicit resource
|
|
98
|
+
and Job-owned Role restrictions still apply. Native conversation tools need no
|
|
99
|
+
Yui gateway or Task mapping; actual resource access and relevant scope suffice.
|
|
100
|
+
|
|
101
|
+
Completed results remain readable. Simple questions need no reopen; the current
|
|
102
|
+
Leader may reopen its own unarchived result when a same-result request or explicit
|
|
103
|
+
unfulfilled commitment needs tracked diagnosis, evidence handoff, authority
|
|
104
|
+
coordination or authorized delivery. Diagnosis stays read-only; reopening retains
|
|
105
|
+
historical completion evidence and never lifts a stop, restores cancelled intent,
|
|
106
|
+
promotes a planning Session or grants external effects.
|
|
107
|
+
|
|
91
108
|
Use `--json` and read the top-level `data`. Run context and expansion use
|
|
92
109
|
`data.context`; Run Context delta uses `data.contextDelta`. For a long detail, repeat that same read with
|
|
93
110
|
`--cursor <contentPage.nextCursor>`. Concatenate `text` in offset order, checking
|
|
@@ -68,6 +68,18 @@ Run 列表接受 Task 或 `task/work` 目标。
|
|
|
68
68
|
决策/里程碑/事件、Role show/status/Session 检查、wake 详情、InputRequest、
|
|
69
69
|
Run Context 与冻结展开使用有界详情读取。
|
|
70
70
|
|
|
71
|
+
普通 Task 读取验证原调用者身份,但不按 Task 或 Assignment ID 隔离数据。Yui 是单用户本地产品,
|
|
72
|
+
资源边界由本地文件权限和已认证服务传输承担,Task 归属不是租户边界。跨 Task 当前 Context
|
|
73
|
+
保留调用者自己的 Task 身份,并标记 `reference-only`。搜索、记录、artifact 和原生会话证据
|
|
74
|
+
不切换身份,不继承其他 Run 的指令,也不授予写入、inbox 处理、派工或外部效果权限。
|
|
75
|
+
明确资源限制和 Job-owned Role 限制仍有效。原生会话读取无需 Yui 网关或 Task 映射;
|
|
76
|
+
应使用实际合法可用的读取工具,并保持工作相关范围。
|
|
77
|
+
|
|
78
|
+
completed 结果仍可读。简单问答无需重开;同结果请求或明确未履行承诺需要持续诊断、
|
|
79
|
+
证据交接、必要权限协调或已授权交付时,当前 Leader 可自行重开自己的未归档结果。
|
|
80
|
+
诊断仍只读;重开保留历史完成证据,不解除停止、不恢复取消意图、不升级 planning
|
|
81
|
+
Session,也不授予外部效果权限。
|
|
82
|
+
|
|
71
83
|
使用 `--json` 读取顶层 `data`;Run Context 和展开使用 `data.context`,Run Context delta 使用 `data.contextDelta`。
|
|
72
84
|
长详情用同一读取命令加 `--cursor <contentPage.nextCursor>` 继续。按 offset 顺序拼接
|
|
73
85
|
`text`,校验相同 source 与 digest,最后一次性解析 JSON。不要分别解析块,也不要把
|
package/docs/release-workflow.md
CHANGED
|
@@ -51,6 +51,60 @@ merely to avoid a version decision. Package versions remain distinct from protoc
|
|
|
51
51
|
and persistent-storage contracts: their compatibility checks and required migrations
|
|
52
52
|
are unchanged, and the storage rules below do not authorize a package release.
|
|
53
53
|
|
|
54
|
+
### 2.21.0 compatibility and migration notes
|
|
55
|
+
|
|
56
|
+
Ordinary Task, Assignment and frozen-context evidence reads now use the local
|
|
57
|
+
resource boundary rather than Task ownership as a confidentiality boundary.
|
|
58
|
+
Catalog/search and relevant records are readable without borrowing the target
|
|
59
|
+
Task's identity or execution authority. Cross-Task context explicitly reports
|
|
60
|
+
`reference-only`. Consumers must not treat readable records, returned instructions,
|
|
61
|
+
or a frozen pack as permission to write, claim input, dispatch, or control work.
|
|
62
|
+
Real user/tenant, filesystem, authenticated transport and explicit read limits
|
|
63
|
+
remain. Native conversation reads use existing legal native tools, not a new gateway.
|
|
64
|
+
|
|
65
|
+
Leader follow-up guidance permits the existing reopen operation for same-result
|
|
66
|
+
tracked diagnosis, evidence handoff, necessary coordination or delivery authorized
|
|
67
|
+
by the user's request or an explicit unfinished commitment. Simple questions stay
|
|
68
|
+
completed, diagnosis stays read-only, and reopen does not lift stop, archive,
|
|
69
|
+
cancelled-intent, Session, workspace or external-effect restrictions. Existing
|
|
70
|
+
reopen and InputRequest operations remain separate; no completed-state write
|
|
71
|
+
exception or automatic replay is introduced.
|
|
72
|
+
|
|
73
|
+
Storage remains **1.25** and Controller/Host protocols remain v1; no migration is
|
|
74
|
+
added. Runtime and Role Skill versions advance independently of the package.
|
|
75
|
+
Exact-merge Core CI and the same runtime archive's fresh-install matrix gate npm
|
|
76
|
+
publication. These deterministic checks do not establish model semantic behavior.
|
|
77
|
+
Publishing does not authorize a global installation update or production restart.
|
|
78
|
+
|
|
79
|
+
### 2.20.0 compatibility and migration notes
|
|
80
|
+
|
|
81
|
+
This minor adds independent optional Role instance codes (`--instance-code` /
|
|
82
|
+
`--clear-instance-code`) and a `Draft` marker for Leader Sessions with planning
|
|
83
|
+
authority. Operator-only configuration does not propagate to Leader or other
|
|
84
|
+
Roles. Task Role template copies remain independent; codes are presentation,
|
|
85
|
+
not identity, authorization or launch-configuration drift. Full lowercase Task
|
|
86
|
+
IDs remain unchanged. See [Role configuration](roles-and-configuration.md).
|
|
87
|
+
|
|
88
|
+
New Sessions use the configured code. Normal Codex Host launches/resumes can refresh
|
|
89
|
+
confirmed Yui-owned titles without forcing a stop/replacement. Historical untagged
|
|
90
|
+
titles and recorded custom names are preserved. Codex resume naming requires
|
|
91
|
+
the native name to match the previous generated title; missing metadata and
|
|
92
|
+
user renames are left untouched. Title provenance advances only after a successful
|
|
93
|
+
native naming receipt, not pre-start registration or live Host reuse. Other
|
|
94
|
+
Providers receive generated names only on creation, never on resume. Provider
|
|
95
|
+
naming remains best effort.
|
|
96
|
+
|
|
97
|
+
Storage advances **1.24 → 1.25** through an additive, no-rewrite transition for
|
|
98
|
+
optional Role code and Session title provenance. Older supported Homes follow
|
|
99
|
+
the centralized contiguous chain; do not open an upgraded Home with older
|
|
100
|
+
software. Controller/Host protocols remain v1. Publication does not authorize
|
|
101
|
+
installing the release, migrating a production Home, restarting shared services,
|
|
102
|
+
replacing Sessions or choosing codes for existing Roles.
|
|
103
|
+
|
|
104
|
+
Release gates require exact-merge Core CI and the same runtime tarball's
|
|
105
|
+
fresh-install matrix before npm `latest` publication. These deterministic
|
|
106
|
+
checks do not establish real-model behavior.
|
|
107
|
+
|
|
54
108
|
### 2.19.0 compatibility and migration notes
|
|
55
109
|
|
|
56
110
|
This minor adds message backlog and blocked-delivery monitoring with Operator
|
|
@@ -36,6 +36,46 @@ Agent 选择一个预先声明的计划,设施从持久状态驱动该计划
|
|
|
36
36
|
不能将不兼容冒称为兼容,也不为回避版本选择新增历史兼容机制。包版本与协议、
|
|
37
37
|
持久存储契约仍然独立:兼容性检查和必要迁移要求不变,下文存储规则不授予发版权限。
|
|
38
38
|
|
|
39
|
+
### 2.21.0 兼容性及迁移说明
|
|
40
|
+
|
|
41
|
+
普通 Task、Assignment 和冻结上下文证据读取改按本地资源边界处理,不再将 Task
|
|
42
|
+
归属本身作为保密边界。目录、搜索及相关记录可读,但不借用目标 Task 身份或执行权;
|
|
43
|
+
跨 Task Context 明确标记 `reference-only`。调用方不得将可读记录、返回的指令或
|
|
44
|
+
冻结包当作写入、领取输入、派发或控制执行的授权。真实用户/租户、文件系统、
|
|
45
|
+
认证传输及明确阅读限制仍保留;原生会话读取复用合法原生工具,不增加专用网关。
|
|
46
|
+
|
|
47
|
+
Leader 可依据同结果用户请求或明确未履行承诺,为需要跟踪的诊断、证据交接、
|
|
48
|
+
必要协调或交付使用既有 reopen。简单问答保持 completed,诊断保持只读;重开
|
|
49
|
+
不解除停止、归档、撤销意图、Session、工作区或外部效果限制。reopen 与 InputRequest
|
|
50
|
+
仍是独立操作,不新增 completed 写入例外或自动重放。
|
|
51
|
+
|
|
52
|
+
存储仍为 **1.25**,Controller/Host 协议仍为 v1,不新增迁移。Runtime 与 Role Skill
|
|
53
|
+
版本独立递增。npm 发布仍要求准确合入 commit 的 Core CI,以及同一 runtime archive
|
|
54
|
+
的全新安装矩阵通过;这些确定性检查不证明模型语义行为。发布不授权全局安装更新
|
|
55
|
+
或生产重启。
|
|
56
|
+
|
|
57
|
+
### 2.20.0 兼容性及迁移说明
|
|
58
|
+
|
|
59
|
+
本 minor 增加按 Role 独立可选的实例码(`--instance-code` /
|
|
60
|
+
`--clear-instance-code`),以及持有 planning 权限的 Leader Session 的 `Draft`
|
|
61
|
+
标识。仅设置 Operator 不会传播到 Leader 或其他 Role;Task Role 模板副本保持
|
|
62
|
+
独立。实例码仅供展示,不改变身份、权限、启动配置版本或完整小写 Task ID。
|
|
63
|
+
详见 [Role 配置](roles-and-configuration.zh-CN.md)。
|
|
64
|
+
|
|
65
|
+
新会话采用配置,正常 Codex Host 启动/恢复可刷新确认由 Yui 管理的标题,不强停或替换会话。
|
|
66
|
+
旧的无来源标记标题和已记录的用户标题保留。Codex 恢复时仅在原生名字仍匹配上次
|
|
67
|
+
生成标题时改名;原生信息缺失或用户已改名则跳过。标题来源仅在原生命名成功回执后
|
|
68
|
+
推进,不因预启动登记或存活 Host 复用而变化。其他 Provider 仅在新建时传入生成名,
|
|
69
|
+
恢复时不传入。Provider 命名仍为尽力能力。
|
|
70
|
+
|
|
71
|
+
存储通过中央连续迁移从 **1.24 → 1.25**,只增加可选实例码和标题来源,
|
|
72
|
+
不改写历史记录。较早的受支持 Home 沿既有迁移链升级,不可用旧程序打开升级后的
|
|
73
|
+
Home。Controller/Host 协议仍为 v1。发布不授权安装升级、迁移生产 Home、
|
|
74
|
+
重启共享服务、替换会话或为现有 Role 选择实例码。
|
|
75
|
+
|
|
76
|
+
发布门禁要求准确合入 commit 的 Core CI 和同一 runtime tarball 的全平台
|
|
77
|
+
fresh-install 矩阵通过后才向 npm `latest` 发布;这些确定性验证不证明真实模型行为。
|
|
78
|
+
|
|
39
79
|
### 2.19.0 兼容性及迁移说明
|
|
40
80
|
|
|
41
81
|
本 minor 新增消息积压、投递阻塞监测及 Operator 告警(PR #492)。观测区分正常执行、
|
|
@@ -18,6 +18,55 @@ requiring a same-named global Role. A global Role is a creation template only
|
|
|
18
18
|
when the requested Task Role does not exist. Availability, producer separation
|
|
19
19
|
and the frozen review candidate still apply.
|
|
20
20
|
|
|
21
|
+
## Optional instance codes and Session titles
|
|
22
|
+
|
|
23
|
+
Each Role can independently set a display-only instance code. For example,
|
|
24
|
+
configure only Operator, leaving Leader and all other Roles unset:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
yui config role update operator --instance-code dev-bj
|
|
28
|
+
yui config role show operator
|
|
29
|
+
yui config role update operator --clear-instance-code
|
|
30
|
+
yui task role update task-159 leader --instance-code dev-sg
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`add` also accepts `--instance-code`. Codes accept 1–24 UTF-16 units of Unicode
|
|
34
|
+
letters, numbers, combining marks, hyphen and underscore; omit `@`. Whitespace,
|
|
35
|
+
controls, escapes and punctuation are rejected, not silently normalized.
|
|
36
|
+
There is no Home-wide fallback. A Global Role's code is copied when that template
|
|
37
|
+
creates a Task Role; later template changes do not rewrite the copy. Agent
|
|
38
|
+
Profiles do not supply display codes. Use Task Role update/clear for an existing
|
|
39
|
+
copy. Codes do not alter launch revisions, permissions, IDs or message routing.
|
|
40
|
+
Choose recognizable values; codes are not globally unique identities. Use
|
|
41
|
+
Session inspection and the actual Home/native identity when resolving ambiguity.
|
|
42
|
+
|
|
43
|
+
Generated Task titles use `Yui Leader task-159 Draft @dev-sg · Task name`.
|
|
44
|
+
`Draft` means the Leader Session has planning authority, including a retained
|
|
45
|
+
planning Session after activation; delivery Sessions omit it. Other Role names
|
|
46
|
+
and the full lowercase Task ID remain intact. Operator uses
|
|
47
|
+
`Yui Operator @dev-bj · MMDD`; without a code its existing
|
|
48
|
+
`Yui · Operator · MMDD` format remains. The date derives from Session creation
|
|
49
|
+
and the configured timezone. Other Global Roles get a generated title when a
|
|
50
|
+
code is configured. Descriptions are truncated first without splitting surrogate
|
|
51
|
+
pairs; if the identity itself cannot fit the 80-unit title limit, launch fails
|
|
52
|
+
with a bounded diagnostic instead of truncating the identity.
|
|
53
|
+
|
|
54
|
+
Configuration does not rename, stop or recreate a live Session. New Sessions
|
|
55
|
+
use it; a subsequent normal Host launch/resume can refresh confirmed Yui-owned titles,
|
|
56
|
+
not historical records. Existing untagged titles and recorded user names stay
|
|
57
|
+
untouched. Codex applies a resume rename only when its returned native name still
|
|
58
|
+
matches the previous generated title; absent metadata or a user rename prevents
|
|
59
|
+
the rename. Codex records only successfully applied names; live Host reuse,
|
|
60
|
+
skipped naming and naming failures do not advance title provenance. Naming is
|
|
61
|
+
best effort; unsupported Providers or failures do not invalidate the Session.
|
|
62
|
+
Other Providers receive generated names only on creation, never on resume,
|
|
63
|
+
to protect native user renames. No restart is performed to force a naming change.
|
|
64
|
+
On the safe Codex resume path, clearing a custom Global Role's code restores
|
|
65
|
+
`Yui role`; clearing Operator's code restores its unconfigured format.
|
|
66
|
+
|
|
67
|
+
Storage 1.24 → 1.25 adds optional code/title provenance without rewriting
|
|
68
|
+
historical payloads or inferring who named an old Session.
|
|
69
|
+
|
|
21
70
|
## Enable and disable
|
|
22
71
|
|
|
23
72
|
Availability is configuration, separate from active Agent selection, Session
|
|
@@ -15,6 +15,44 @@ Task Role 是 Task 局部的;global Role 提供已配置的默认值和全局
|
|
|
15
15
|
的 global Role。只有当请求的 Task Role 不存在时,global Role 才作为创建模板。
|
|
16
16
|
可用性、producer 分离和冻结的审查候选仍然适用。
|
|
17
17
|
|
|
18
|
+
## 可选实例码与会话标题
|
|
19
|
+
|
|
20
|
+
每个 Role 独立配置仅用于展示的实例码。例如只设置 Operator,其他角色留空:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
yui config role update operator --instance-code dev-bj
|
|
24
|
+
yui config role show operator
|
|
25
|
+
yui config role update operator --clear-instance-code
|
|
26
|
+
yui task role update task-159 leader --instance-code dev-sg
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`add` 也接受 `--instance-code`。允许 1–24 个 UTF-16 单位的 Unicode 字母、
|
|
30
|
+
数字、组合标记、短横线和下划线,不输入 `@`。空白、控制字符、转义和其他标点
|
|
31
|
+
直接拒绝,不静默清洗。没有 Home 级默认值;Global Role 作为模板创建 Task Role
|
|
32
|
+
时复制其值,之后模板变化不回写。Agent Profile 不携带实例码;既有 Task Role
|
|
33
|
+
通过自己的 update/clear 修改。实例码不改变启动版本、权限、内部 ID 或消息寻址,
|
|
34
|
+
也不保证全局唯一;遇到歧义应查看实际 Home、Session 和原生身份。
|
|
35
|
+
|
|
36
|
+
生成的 Task 标题为 `Yui Leader task-159 Draft @dev-sg · 任务名`。
|
|
37
|
+
`Draft` 根据 Leader Session 的 planning 权限显示;Task 激活后保留的 planning
|
|
38
|
+
会话仍显示 Draft,delivery 会话不显示。保留完整小写 Task ID。
|
|
39
|
+
Operator 为 `Yui Operator @dev-bj · MMDD`;未配置保持
|
|
40
|
+
`Yui · Operator · MMDD`,日期使用会话创建时间和配置时区。
|
|
41
|
+
其他 Global Role 配置实例码后生成标题。80 单位总长度内优先截断任务名,
|
|
42
|
+
不拆开代理对;身份部分无法容纳则明确报错,不静默截断。
|
|
43
|
+
|
|
44
|
+
配置修改不会立即重命名、停止或重建活动会话。新会话采用新配置,后续正常
|
|
45
|
+
Host 启动/恢复可刷新确认由 Yui 管理的标题,不改写历史记录。
|
|
46
|
+
缺少来源标记的旧标题及已记录的用户标题保留。Codex 恢复时只有原生返回的
|
|
47
|
+
名字仍等于上次生成标题才改名;原生信息缺失或用户已改名则跳过。
|
|
48
|
+
Codex 只记录已成功应用的名称;复用存活 Host、命名失败或跳过不推进标题来源。
|
|
49
|
+
Provider 命名为尽力能力;失败或不支持不会使会话失效。其他 Provider 仅在新建时
|
|
50
|
+
传入生成名,恢复时不重新传入名称,以保护原生用户改名;不为改名主动重启。
|
|
51
|
+
在可安全改名的 Codex 恢复路径,清除自定义 Global Role 实例码后恢复
|
|
52
|
+
`Yui role`;Operator 恢复未配置格式。
|
|
53
|
+
|
|
54
|
+
存储 1.24 → 1.25 增加可选实例码和生成标题来源,不回填历史记录或猜测标题所有权。
|
|
55
|
+
|
|
18
56
|
## 启用与停用
|
|
19
57
|
|
|
20
58
|
启停是配置状态,与 Active Agent、Session 是否运行和 Task executionGate 分开:
|
package/docs/storage-baseline.md
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
# Storage baseline 1.0
|
|
4
4
|
|
|
5
|
-
Current storage is 1.
|
|
5
|
+
Current storage is 1.25. The 1.24 → 1.25 transition admits optional Role
|
|
6
|
+
`instanceCode` and Session `generatedTitle` presentation fields without rewriting
|
|
7
|
+
existing records. Untagged historical titles remain user-owned; no code or
|
|
8
|
+
title provenance is inferred. The 1.23 → 1.24 transition adds transaction-maintained
|
|
6
9
|
Task read-cache revisions for the scheduler's nine source tables. Inserts,
|
|
7
10
|
updates and deletions invalidate only the affected Task; rollback also rolls
|
|
8
11
|
back the token. These tokens are derived cache identities, never Task authority
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# 存储基线 1.0
|
|
4
4
|
|
|
5
|
-
当前存储为 1.
|
|
5
|
+
当前存储为 1.25。1.24 → 1.25 增加可选 Role `instanceCode` 和 Session `generatedTitle` 展示字段,不回填历史标题来源或实例码,不改写既有记录。1.23 → 1.24 为调度器的九类来源表增加事务维护的 Task 读取缓存修订号。插入、更新和删除只使对应 Task 的缓存失效,回滚同时回滚修订号。该值仅是派生缓存标识,不是 Task 权限或 Home revision CAS 的替代。历史 payload 不变,旧 Task 在首次修改前使用隐含的零修订号。
|
|
6
6
|
同时增加两个局部索引,支持按精确运行观测身份查找,不再扫描或反序列化历史观测集合。
|
|
7
7
|
|
|
8
8
|
1.22 → 1.23 新增全局 Shell Job、版本化脚本文件及每次运行的配置快照,不改写已有 Job、计划和历史记录。
|
package/docs/task-discovery.md
CHANGED
|
@@ -116,11 +116,11 @@ source records; no report text is parsed into status. Web explicitly labels
|
|
|
116
116
|
these facts and offers category navigation. Selected detail survives paging,
|
|
117
117
|
filtering and refresh; only the detail read can establish absence.
|
|
118
118
|
|
|
119
|
-
Task
|
|
120
|
-
|
|
121
|
-
the catalog
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
Task Leaders, Workers and Reviewers may discover relevant ordinary records
|
|
120
|
+
under the local resource's access rules; Task/Assignment ownership does not
|
|
121
|
+
filter the catalog. Global non-Operator and incomplete or unknown managed
|
|
122
|
+
identities remain rejected. Historical readers retain read-only identity,
|
|
123
|
+
never another Task's execution authority. A cursor is scope/filter-bound continuation,
|
|
124
124
|
not a credential. Read operations do not consume messages, acknowledge jobs,
|
|
125
125
|
wake Roles or query providers.
|
|
126
126
|
|
|
@@ -73,10 +73,10 @@ Brief。当前 Brief 通过 Task Context 读取;Context inspect 可校验 dige
|
|
|
73
73
|
不解析报告正文推导状态。Web 明确标示这些事实并提供分类导航。
|
|
74
74
|
已选详情不因分页、筛选或刷新而丢失;只有详情读取可以确认目标不存在。
|
|
75
75
|
|
|
76
|
-
Task Leader
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
Task Leader、Worker 和 Reviewer 可按本地资源访问规则发现相关普通记录;
|
|
77
|
+
目录不以 Task/Assignment 归属过滤数据。非 Operator 的 Global Session、
|
|
78
|
+
不完整或未知的受管身份仍被拒绝。历史读取者保持只读身份,不获得其他 Task 的执行权。
|
|
79
|
+
游标绑定范围及参数,不是凭证。
|
|
80
80
|
读取不会消费 Message、确认 Job、唤醒 Role 或查询外部 provider。
|
|
81
81
|
|
|
82
82
|
## 成本边界
|