@zq-silk/yui 0.15.9 → 0.15.12
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/ARCHITECTURE.md +8 -4
- package/ARCHITECTURE.zh-CN.md +5 -2
- package/README.md +13 -5
- package/dist/agent/launchEnvironment.js +7 -0
- package/dist/agentRun/agentRun.js +3 -0
- package/dist/cli/commandCatalog.js +64 -16
- package/dist/cli/interactionPolicy.js +7 -3
- package/dist/cli/managedDiagnostics.js +1 -1
- package/dist/cli/updateOrchestrator.js +24 -1
- package/dist/cli/updatePorts.js +7 -3
- package/dist/cli/upgradeCommand.js +42 -2
- package/dist/cli.js +381 -107
- package/dist/commands/executionAuditCommands.js +10 -0
- package/dist/commands/globalRoleCommands.js +339 -4
- package/dist/commands/projectCommands.js +50 -22
- package/dist/commands/releaseCommands.js +18 -0
- package/dist/commands/taskActor.js +25 -0
- package/dist/commands/taskCommands.js +586 -96
- package/dist/commands/taskIntegrationCommands.js +19 -39
- package/dist/commands/taskIntegrationQueueCommands.js +1 -1
- package/dist/commands/taskOverviewCommand.js +4 -3
- package/dist/commands/taskPublicationAdoptCommand.js +127 -0
- package/dist/commands/taskPublicationCommands.js +11 -2
- package/dist/commands/taskPublicationVerifyCommand.js +23 -39
- package/dist/commands/taskRemoteDeliveryCommand.js +22 -11
- package/dist/commands/taskRoleRuntimeStatus.js +35 -0
- package/dist/context/runContextPack.js +3 -0
- package/dist/context/taskCatalog.js +187 -0
- package/dist/context/taskContext.js +55 -6
- package/dist/controller/agentHostObservation.js +155 -0
- package/dist/controller/clientRuntime.js +17 -2
- package/dist/controller/controller.js +14 -2
- package/dist/controller/fileSchedulerStoreAdapter.js +519 -25
- package/dist/controller/globalInputDelivery.js +132 -0
- package/dist/controller/jobControl.js +6 -2
- package/dist/controller/providerRetryAdmission.js +100 -0
- package/dist/controller/providerRetryDelivery.js +218 -0
- package/dist/controller/resourceInventory.js +14 -4
- package/dist/controller/resourceInventoryLinux.js +2 -6
- package/dist/controller/runtime.js +117 -7
- package/dist/controller/runtimeEventInbox.js +32 -3
- package/dist/controller/runtimeEventProcessor.js +26 -6
- package/dist/controller/runtimeHookRunFence.js +75 -19
- package/dist/controller/structuredProviderObservation.js +133 -70
- package/dist/coordination/workMailboxQueue.js +5 -0
- package/dist/execution/workItemExecutionProjection.js +1 -1
- package/dist/executor/agentExecutor.js +64 -4
- package/dist/executor/executorRegistry.js +3 -0
- package/dist/executor/fileRoleLaunchPlanner.js +78 -118
- package/dist/integration/deliveryObligation.js +2 -1
- package/dist/integration/gitIntegrationService.js +329 -386
- package/dist/integration/integrationAttempt.js +30 -4
- package/dist/integration/integrationQueueService.js +7 -7
- package/dist/integration/integrationSourceApplication.js +323 -0
- package/dist/lifecycle/exactRunTerminalization.js +4 -1
- package/dist/message/globalInterrupt.js +33 -0
- package/dist/message/globalProviderRetry.js +15 -0
- package/dist/message/inputControlResolution.js +106 -0
- package/dist/message/message.js +367 -0
- package/dist/message/messageContinuation.js +126 -3
- package/dist/message/taskInterrupt.js +34 -0
- package/dist/observability/executionAudit.js +19 -0
- package/dist/observability/orchestrationMetrics.js +1 -1
- package/dist/release/releaseHandover.js +22 -0
- package/dist/release/releaseWorkflowPorts.js +15 -7
- package/dist/repository/gitWorkspace.js +430 -107
- package/dist/repository/projectMaintenanceLock.js +75 -18
- package/dist/repository/taskWorkspaceCoordinator.js +182 -101
- package/dist/repository/taskWorkspacePreparer.js +205 -72
- package/dist/repository/workItemCandidateSnapshot.js +34 -0
- package/dist/repository/workspaceCleanupInspection.js +187 -0
- package/dist/resources/resourceDiscovery.js +3 -2
- package/dist/runtime/agentError.js +5 -3
- package/dist/runtime/agentHost.js +179 -82
- package/dist/runtime/agentHostCompatibility.js +127 -0
- package/dist/runtime/agentHostProtocol.js +53 -0
- package/dist/runtime/builtinAgentErrorMappers.js +91 -0
- package/dist/runtime/codexAppServerRuntime.js +34 -3
- package/dist/runtime/executionEnvironment.js +0 -19
- package/dist/runtime/launchBroker.js +6 -0
- package/dist/runtime/providerControl.js +5 -1
- package/dist/runtime/providerRetry.js +198 -0
- package/dist/runtime/providerRuntimeIdentity.js +28 -2
- package/dist/runtime/sessionReconciliation.js +4 -4
- package/dist/runtime/sessionTokenMetrics.js +15 -5
- package/dist/runtime/structuredProviderHost.js +6 -2
- package/dist/runtime/taskRuntimeIsolation.js +30 -6
- package/dist/runtime/taskUsageMetrics.js +275 -0
- package/dist/runtime/tmuxAdapters.js +5 -3
- package/dist/scheduler/activeRoleRunDelivery.js +12 -0
- package/dist/scheduler/leaderWakeupProcessor.js +5 -0
- package/dist/scheduler/operatorEvent.js +4 -0
- package/dist/scheduler/taskExecutionProjection.js +38 -6
- package/dist/scheduler/taskObservabilityProjection.js +6 -44
- package/dist/scheduler/wakeReason.js +7 -1
- package/dist/scheduler/wakeupQueue.js +2 -0
- package/dist/setup/setupCommand.js +26 -8
- package/dist/storage/homeLayout.js +130 -0
- package/dist/storage/migrations/collapseWorktreeLayout.js +963 -0
- package/dist/storage/migrations/integrationContinuation.js +104 -0
- package/dist/storage/migrations/unifyHomeLayout.js +925 -0
- package/dist/storage/sqliteSchema.js +167 -4
- package/dist/storage/sqliteStore.js +57 -1
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +2 -0
- package/dist/storage/taskCatalog.js +123 -0
- package/dist/storage/taskStore.js +2 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +95 -2
- package/dist/task/archiveDiagnostics.js +129 -0
- package/dist/task/archivePreflight.js +124 -0
- package/dist/task/nextAction.js +44 -11
- package/dist/task/publicationAdoption.js +56 -0
- package/dist/task/publicationReference.js +10 -0
- package/dist/task/remoteDelivery.js +31 -16
- package/dist/web/assets/client/app.js +147 -17
- package/dist/web/assets/client/components.js +56 -13
- package/dist/web/assets/client/i18n.js +78 -4
- package/dist/web/assets/client/taskSurface.js +108 -1
- package/dist/web/assets/client/view.js +39 -8
- package/dist/web/assets/shell.js +29 -0
- package/dist/web/assets/styles/layout.js +8 -1
- package/dist/web/assets/styles/widgets.js +12 -0
- package/dist/web/webServer.js +131 -4
- package/dist/web/webSnapshot.js +16 -6
- package/dist/web/webTaskSurface.js +222 -5
- package/dist/workspace/cleanupInspection.js +63 -0
- package/dist/workspace/workItemChangeSetManager.js +111 -35
- package/docs/agent-result-consumption.md +4 -0
- package/docs/agent-result-consumption.zh-CN.md +3 -0
- package/docs/agent-runtime-drivers.md +7 -0
- package/docs/agent-runtime-drivers.zh-CN.md +5 -0
- package/docs/architecture/README.md +2 -0
- package/docs/architecture/README.zh-CN.md +3 -1
- package/docs/architecture/capabilities-and-resources.md +30 -5
- package/docs/architecture/capabilities-and-resources.zh-CN.md +23 -3
- package/docs/managed-turn-and-session-runtime.md +47 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +40 -0
- package/docs/observability/README.md +62 -0
- package/docs/observability/README.zh-CN.md +47 -0
- package/docs/project-refresh.md +77 -0
- package/docs/project-refresh.zh-CN.md +59 -0
- package/docs/provider-retry.md +70 -0
- package/docs/release-workflow.md +39 -0
- package/docs/release-workflow.zh-CN.md +29 -0
- package/docs/sqlite-control-plane-design.md +223 -1
- package/docs/task-delivery.md +133 -13
- package/docs/task-delivery.zh-CN.md +99 -10
- package/docs/task-discovery.md +102 -0
- package/docs/task-discovery.zh-CN.md +86 -0
- package/docs/testing/verification-levels.md +40 -0
- package/docs/testing/verification-levels.zh-CN.md +23 -0
- package/i18n/README.zh-CN.md +13 -7
- package/package.json +1 -1
- package/skills/yui-leader/references/execution.md +154 -51
- package/skills/yui-leader/references/integration.md +52 -2
- package/skills/yui-operator/SKILL.md +19 -3
- package/skills/yui-reviewer/SKILL.md +4 -0
- package/skills/yui-runtime/SKILL.md +42 -0
- package/skills/yui-runtime/references/publication.md +42 -0
- package/skills/yui-runtime/references/recovery.md +24 -0
|
@@ -7,39 +7,70 @@ import { createRuntimeObservation, runtimeObservationSemanticKey } from "../runt
|
|
|
7
7
|
import { runtimeLifecycleSignalKey } from "../runtime/lifecycleReservation.js";
|
|
8
8
|
import { isForeignHandoverLockHeld } from "../release/runtimeRelease.js";
|
|
9
9
|
import { FileRuntimeEventInbox } from "./runtimeEventInbox.js";
|
|
10
|
-
import {
|
|
10
|
+
import { AGENT_HOST_EVENT_PROTOCOL } from "../runtime/agentHostProtocol.js";
|
|
11
|
+
import { redactAgentErrorText } from "../runtime/agentError.js";
|
|
11
12
|
let structuredSequence = 0;
|
|
13
|
+
let deliveryFailure;
|
|
14
|
+
// Only live diagnostics for this process's immutable files, not another queue.
|
|
15
|
+
// No payload, retry, or authoritative delivery state is kept in this map.
|
|
16
|
+
const emittedEvents = new Map();
|
|
17
|
+
/** A live transport observation, independent of Provider state and Run truth. */
|
|
18
|
+
export function structuredProviderEventDelivery(home, environment, nativeSessionId) {
|
|
19
|
+
try {
|
|
20
|
+
const inbox = new FileRuntimeEventInbox(home);
|
|
21
|
+
const pending = [];
|
|
22
|
+
for (const [id, entry] of emittedEvents) {
|
|
23
|
+
if (entry.home !== home)
|
|
24
|
+
continue;
|
|
25
|
+
if (!inbox.has(id)) {
|
|
26
|
+
emittedEvents.delete(id);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (entry.taskId === environment.YUI_TASK_ID && entry.roleName === environment.YUI_ROLE
|
|
30
|
+
&& (nativeSessionId === undefined || entry.nativeSessionId === nativeSessionId))
|
|
31
|
+
pending.push(entry);
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
pending: pending.length,
|
|
35
|
+
pendingTerminals: pending.filter(event => event.terminal).length,
|
|
36
|
+
...(deliveryFailure === undefined ? {} : { failure: deliveryFailure })
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return { pending: 0, pendingTerminals: 0, failure: deliveryFault("persistence", error) };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function deliveryFault(stage, error) {
|
|
44
|
+
return {
|
|
45
|
+
stage, detail: redactAgentErrorText(error instanceof Error ? error.message : String(error)),
|
|
46
|
+
observedAt: new Date().toISOString()
|
|
47
|
+
};
|
|
48
|
+
}
|
|
12
49
|
export async function publishStructuredProviderAttachmentExit(input) {
|
|
13
50
|
const adapterId = requireIdentity(input.environment.YUI_ADAPTER_ID, "Agent adapter id");
|
|
14
51
|
const driver = builtinAgentDriverRegistry().requireByAdapterId(adapterId);
|
|
15
|
-
const owner =
|
|
16
|
-
terminal: true, attemptId: input.attemptId, nativeTurnId: input.nativeTurnId
|
|
17
|
-
});
|
|
52
|
+
const owner = describeHostIdentity(input.environment, adapterId, input.nativeSessionId);
|
|
18
53
|
await persistAndApply(input.home, [observation({
|
|
19
54
|
kind: input.failed ? "session.failed" : "session.ended", observedAt: input.observedAt,
|
|
20
55
|
sequence: nextStructuredSequence(), ordinal: 0,
|
|
21
56
|
fence: {
|
|
22
57
|
taskId: owner.taskId, roleName: owner.roleName, agentId: owner.agentId, driverId: driver.id,
|
|
23
58
|
nativeSessionId: input.nativeSessionId, conversationId: input.nativeSessionId,
|
|
24
|
-
...(owner.runId === undefined ? {} : { runId: owner.runId }),
|
|
25
59
|
...(input.attemptId === undefined ? {} : { receiptId: input.attemptId }),
|
|
26
60
|
...(input.nativeTurnId === undefined ? {} : { nativeTurnId: input.nativeTurnId })
|
|
27
61
|
}
|
|
28
|
-
})],
|
|
62
|
+
})], input.environment);
|
|
29
63
|
}
|
|
30
64
|
export async function publishStructuredProviderActivity(input) {
|
|
31
65
|
const activity = input.activity;
|
|
32
66
|
const adapterId = requireIdentity(input.environment.YUI_ADAPTER_ID, "Agent adapter id");
|
|
33
67
|
const driver = builtinAgentDriverRegistry().requireByAdapterId(adapterId);
|
|
34
|
-
const owner =
|
|
35
|
-
nativeTurnId: activity.nativeTurnId, attemptId: activity.attemptId
|
|
36
|
-
});
|
|
68
|
+
const owner = describeHostIdentity(input.environment, adapterId, activity.nativeSessionId);
|
|
37
69
|
const event = observation({
|
|
38
70
|
kind: activity.phase === "model" ? "activity.observed" : `operation.${activity.phase}`,
|
|
39
71
|
observedAt: activity.observedAt, sequence: nextStructuredSequence(), ordinal: 0,
|
|
40
72
|
fence: {
|
|
41
73
|
taskId: owner.taskId, roleName: owner.roleName,
|
|
42
|
-
...(owner.runId === undefined ? {} : { runId: owner.runId }),
|
|
43
74
|
agentId: owner.agentId, driverId: driver.id,
|
|
44
75
|
nativeSessionId: activity.nativeSessionId, conversationId: activity.conversationId,
|
|
45
76
|
receiptId: activity.attemptId,
|
|
@@ -49,7 +80,7 @@ export async function publishStructuredProviderActivity(input) {
|
|
|
49
80
|
? { activity: "model", activityId: activity.id }
|
|
50
81
|
: { operation: "tool", operationId: activity.id }
|
|
51
82
|
});
|
|
52
|
-
await persistAndApply(input.home, [event],
|
|
83
|
+
await persistAndApply(input.home, [event], input.environment);
|
|
53
84
|
}
|
|
54
85
|
/** An exact provider item id is evidence of visible input, not an inferred
|
|
55
86
|
* human author. The original managed request is never replaced.
|
|
@@ -57,28 +88,21 @@ export async function publishStructuredProviderActivity(input) {
|
|
|
57
88
|
export async function publishStructuredProviderInputObserved(input) {
|
|
58
89
|
const observed = input.observed;
|
|
59
90
|
const adapterId = requireIdentity(input.environment.YUI_ADAPTER_ID, "Agent adapter id");
|
|
60
|
-
const fence =
|
|
61
|
-
nativeTurnId: observed.nativeTurnId, terminal: true, sessionOnly: true
|
|
62
|
-
});
|
|
63
|
-
if (fence.runId === undefined)
|
|
64
|
-
return; // Ordinary chat need not be mirrored.
|
|
91
|
+
const fence = describeHostIdentity(input.environment, adapterId, observed.nativeSessionId);
|
|
65
92
|
await persistAndApply(input.home, [observation({
|
|
66
93
|
kind: "input.accepted", observedAt: observed.observedAt, sequence: nextStructuredSequence(), ordinal: 0,
|
|
67
94
|
fence: { ...fence, driverId: builtinAgentDriverRegistry().requireByAdapterId(adapterId).id,
|
|
68
95
|
conversationId: observed.conversationId, nativeTurnId: observed.nativeTurnId,
|
|
69
96
|
receiptId: `native-input:${observed.nativeTurnId}/${observed.inputId}` },
|
|
70
97
|
payload: { input: observed.input }
|
|
71
|
-
})],
|
|
98
|
+
})], input.environment);
|
|
72
99
|
}
|
|
73
100
|
export async function publishStructuredProviderStarted(input) {
|
|
74
101
|
if (input.started.clientOwned)
|
|
75
102
|
return;
|
|
76
103
|
const adapterId = requireIdentity(input.environment.YUI_ADAPTER_ID, "Agent adapter id");
|
|
77
104
|
const driver = builtinAgentDriverRegistry().requireByAdapterId(adapterId);
|
|
78
|
-
const fence =
|
|
79
|
-
nativeTurnId: input.started.nativeTurnId,
|
|
80
|
-
sessionOnly: true
|
|
81
|
-
});
|
|
105
|
+
const fence = describeHostIdentity(input.environment, adapterId, input.started.nativeSessionId);
|
|
82
106
|
const receiptId = `direct:${input.started.nativeTurnId}`;
|
|
83
107
|
const startedObservation = observation({
|
|
84
108
|
kind: "turn.accepted",
|
|
@@ -97,22 +121,16 @@ export async function publishStructuredProviderStarted(input) {
|
|
|
97
121
|
},
|
|
98
122
|
payload: input.started.input === undefined ? {} : { input: input.started.input }
|
|
99
123
|
});
|
|
100
|
-
await persistAndApply(input.home, [startedObservation],
|
|
124
|
+
await persistAndApply(input.home, [startedObservation], input.environment);
|
|
101
125
|
}
|
|
102
126
|
export async function publishStructuredProviderAccepted(input) {
|
|
103
127
|
const adapterId = requireIdentity(input.environment.YUI_ADAPTER_ID, "Agent adapter id");
|
|
104
128
|
const driver = builtinAgentDriverRegistry().requireByAdapterId(adapterId);
|
|
105
|
-
const
|
|
106
|
-
const fence = resolveRuntimeHookRunFence(input.environment, adapterId, input.receipt.nativeSessionId, {
|
|
107
|
-
startupSession,
|
|
108
|
-
nativeTurnId: input.receipt.nativeTurnId,
|
|
109
|
-
attemptId: input.receipt.attemptId
|
|
110
|
-
});
|
|
129
|
+
const fence = describeHostIdentity(input.environment, adapterId, input.receipt.nativeSessionId);
|
|
111
130
|
const observedAt = input.receipt.acceptedAt;
|
|
112
131
|
const commonFence = {
|
|
113
132
|
taskId: fence.taskId,
|
|
114
133
|
roleName: fence.roleName,
|
|
115
|
-
...(fence.runId === undefined ? {} : { runId: fence.runId }),
|
|
116
134
|
agentId: fence.agentId,
|
|
117
135
|
driverId: driver.id,
|
|
118
136
|
conversationId: input.receipt.conversationId,
|
|
@@ -136,18 +154,15 @@ export async function publishStructuredProviderAccepted(input) {
|
|
|
136
154
|
ordinal: 0,
|
|
137
155
|
fence: commonFence
|
|
138
156
|
})];
|
|
139
|
-
await persistAndApply(input.home, observations,
|
|
157
|
+
await persistAndApply(input.home, observations, input.environment);
|
|
140
158
|
}
|
|
141
159
|
/** Steer settles an additional input, never the parent AgentRun's initial delivery. */
|
|
142
160
|
export async function publishStructuredProviderInputSettlement(input) {
|
|
143
161
|
const adapterId = requireIdentity(input.environment.YUI_ADAPTER_ID, "Agent adapter id");
|
|
144
162
|
const driver = builtinAgentDriverRegistry().requireByAdapterId(adapterId);
|
|
145
|
-
const fence =
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
});
|
|
149
|
-
if (fence.runId === undefined)
|
|
150
|
-
throw new Error("Steer settlement has no exact original AgentRun.");
|
|
163
|
+
const fence = describeHostIdentity(input.environment, adapterId, input.nativeSessionId);
|
|
164
|
+
// The Host reports the exact Session/turn/attempt, including input with no
|
|
165
|
+
// AgentRun. Only the Controller resolves a current Run from durable state.
|
|
151
166
|
const entry = observation({
|
|
152
167
|
kind: input.status === "accepted" ? "input.accepted"
|
|
153
168
|
: input.status === "rejected" ? "input.rejected" : "input.delivery-unknown",
|
|
@@ -157,7 +172,6 @@ export async function publishStructuredProviderInputSettlement(input) {
|
|
|
157
172
|
fence: {
|
|
158
173
|
taskId: fence.taskId,
|
|
159
174
|
roleName: fence.roleName,
|
|
160
|
-
runId: fence.runId,
|
|
161
175
|
agentId: fence.agentId,
|
|
162
176
|
driverId: driver.id,
|
|
163
177
|
conversationId: input.nativeSessionId,
|
|
@@ -180,22 +194,20 @@ export async function publishStructuredProviderInputSettlement(input) {
|
|
|
180
194
|
})
|
|
181
195
|
}
|
|
182
196
|
});
|
|
183
|
-
await persistAndApply(input.home, [entry],
|
|
197
|
+
await persistAndApply(input.home, [entry], input.environment);
|
|
184
198
|
}
|
|
185
199
|
export async function publishStructuredProviderOpened(input) {
|
|
186
200
|
const adapterId = requireIdentity(input.environment.YUI_ADAPTER_ID, "Agent adapter id");
|
|
187
201
|
const driver = builtinAgentDriverRegistry().requireByAdapterId(adapterId);
|
|
188
202
|
const startupSession = driver.capabilities.observation.sessionBootstrap;
|
|
189
|
-
const fence =
|
|
203
|
+
const fence = describeHostIdentity(input.environment, adapterId, input.nativeSessionId);
|
|
190
204
|
const commonFence = {
|
|
191
205
|
taskId: fence.taskId,
|
|
192
206
|
roleName: fence.roleName,
|
|
193
|
-
...(fence.runId === undefined ? {} : { runId: fence.runId }),
|
|
194
207
|
agentId: fence.agentId,
|
|
195
208
|
driverId: driver.id,
|
|
196
209
|
conversationId: input.conversationId,
|
|
197
|
-
nativeSessionId: input.nativeSessionId
|
|
198
|
-
...(fence.receiptId === undefined ? {} : { receiptId: fence.receiptId })
|
|
210
|
+
nativeSessionId: input.nativeSessionId
|
|
199
211
|
};
|
|
200
212
|
const sequence = nextStructuredSequence();
|
|
201
213
|
const observations = [observation({
|
|
@@ -212,12 +224,12 @@ export async function publishStructuredProviderOpened(input) {
|
|
|
212
224
|
fence: commonFence,
|
|
213
225
|
payload: { recoverability: input.recoverability }
|
|
214
226
|
})];
|
|
215
|
-
await persistAndApply(input.home, observations,
|
|
227
|
+
await persistAndApply(input.home, observations, input.environment, undefined, input.startupRunId);
|
|
216
228
|
}
|
|
217
229
|
export async function publishStructuredProviderGoal(input) {
|
|
218
230
|
const adapterId = requireIdentity(input.environment.YUI_ADAPTER_ID, "Agent adapter id");
|
|
219
231
|
const driver = builtinAgentDriverRegistry().requireByAdapterId(adapterId);
|
|
220
|
-
const fence =
|
|
232
|
+
const fence = describeHostIdentity(input.environment, adapterId, input.conversationId);
|
|
221
233
|
const goal = input.goal;
|
|
222
234
|
const observedAt = input.observedAt ?? goal?.updatedAt ?? new Date().toISOString();
|
|
223
235
|
const goalObservation = observation({
|
|
@@ -241,22 +253,20 @@ export async function publishStructuredProviderGoal(input) {
|
|
|
241
253
|
...(goal.tokenBudget === undefined ? {} : { goalTokenBudget: goal.tokenBudget })
|
|
242
254
|
}
|
|
243
255
|
});
|
|
244
|
-
await persistAndApply(input.home, [goalObservation],
|
|
256
|
+
await persistAndApply(input.home, [goalObservation], input.environment);
|
|
245
257
|
}
|
|
246
258
|
export async function publishStructuredConversationRecoverability(input) {
|
|
247
259
|
const adapterId = requireIdentity(input.environment.YUI_ADAPTER_ID, "Agent adapter id");
|
|
248
260
|
const driver = builtinAgentDriverRegistry().requireByAdapterId(adapterId);
|
|
249
|
-
const fence =
|
|
261
|
+
const fence = describeHostIdentity(input.environment, adapterId, input.conversationId);
|
|
250
262
|
const sequence = nextStructuredSequence();
|
|
251
263
|
const observationFence = {
|
|
252
264
|
taskId: fence.taskId,
|
|
253
265
|
roleName: fence.roleName,
|
|
254
|
-
...(fence.runId === undefined ? {} : { runId: fence.runId }),
|
|
255
266
|
agentId: fence.agentId,
|
|
256
267
|
driverId: driver.id,
|
|
257
268
|
conversationId: input.conversationId,
|
|
258
|
-
nativeSessionId: input.conversationId
|
|
259
|
-
...(fence.receiptId === undefined ? {} : { receiptId: fence.receiptId })
|
|
269
|
+
nativeSessionId: input.conversationId
|
|
260
270
|
};
|
|
261
271
|
const observations = [observation({
|
|
262
272
|
kind: "conversation.observed",
|
|
@@ -266,17 +276,12 @@ export async function publishStructuredConversationRecoverability(input) {
|
|
|
266
276
|
fence: observationFence,
|
|
267
277
|
payload: { recoverability: input.recoverability }
|
|
268
278
|
})];
|
|
269
|
-
await persistAndApply(input.home, observations,
|
|
279
|
+
await persistAndApply(input.home, observations, input.environment);
|
|
270
280
|
}
|
|
271
281
|
export async function publishStructuredProviderTerminal(input) {
|
|
272
282
|
const adapterId = requireIdentity(input.environment.YUI_ADAPTER_ID, "Agent adapter id");
|
|
273
283
|
const driver = builtinAgentDriverRegistry().requireByAdapterId(adapterId);
|
|
274
|
-
const fence =
|
|
275
|
-
terminal: true,
|
|
276
|
-
nativeTurnId: input.terminal.nativeTurnId,
|
|
277
|
-
attemptId: input.terminal.attemptId,
|
|
278
|
-
...(input.terminal.clientOwned ? {} : { sessionOnly: true })
|
|
279
|
-
});
|
|
284
|
+
const fence = describeHostIdentity(input.environment, adapterId, input.terminal.nativeSessionId);
|
|
280
285
|
const kind = input.terminal.status === "completed"
|
|
281
286
|
? "turn.completed"
|
|
282
287
|
: input.terminal.status === "cancelled" ? "turn.cancelled" : "turn.failed";
|
|
@@ -321,7 +326,6 @@ export async function publishStructuredProviderTerminal(input) {
|
|
|
321
326
|
fence: {
|
|
322
327
|
taskId: fence.taskId,
|
|
323
328
|
roleName: fence.roleName,
|
|
324
|
-
...(fence.runId === undefined ? {} : { runId: fence.runId }),
|
|
325
329
|
agentId: fence.agentId,
|
|
326
330
|
driverId: driver.id,
|
|
327
331
|
conversationId: input.terminal.conversationId,
|
|
@@ -329,9 +333,7 @@ export async function publishStructuredProviderTerminal(input) {
|
|
|
329
333
|
...(input.terminal.nativeTurnId === undefined ? {} : {
|
|
330
334
|
nativeTurnId: input.terminal.nativeTurnId
|
|
331
335
|
}),
|
|
332
|
-
...(input.terminal.attemptId === undefined
|
|
333
|
-
? fence.receiptId === undefined ? {} : { receiptId: fence.receiptId }
|
|
334
|
-
: { receiptId: input.terminal.attemptId })
|
|
336
|
+
...(input.terminal.attemptId === undefined ? {} : { receiptId: input.terminal.attemptId })
|
|
335
337
|
},
|
|
336
338
|
payload: {
|
|
337
339
|
...payload,
|
|
@@ -340,7 +342,7 @@ export async function publishStructuredProviderTerminal(input) {
|
|
|
340
342
|
...(input.terminal.input === undefined ? {} : { input: input.terminal.input })
|
|
341
343
|
}
|
|
342
344
|
});
|
|
343
|
-
await persistAndApply(input.home, [terminalObservation],
|
|
345
|
+
await persistAndApply(input.home, [terminalObservation], input.environment);
|
|
344
346
|
}
|
|
345
347
|
function observation(input) {
|
|
346
348
|
const eventId = `agent-host-${randomUUID()}`;
|
|
@@ -367,7 +369,8 @@ function observation(input) {
|
|
|
367
369
|
}
|
|
368
370
|
async function signalController(home, taskId, roleName) {
|
|
369
371
|
await callController(home, "scheduler.signal", {
|
|
370
|
-
key: runtimeLifecycleSignalKey(
|
|
372
|
+
key: runtimeLifecycleSignalKey(taskId === undefined
|
|
373
|
+
? { scope: "global", roleName } : { scope: "task", taskId, roleName })
|
|
371
374
|
}, { timeoutMs: 100 }).catch(() => { });
|
|
372
375
|
}
|
|
373
376
|
function nextStructuredSequence() {
|
|
@@ -380,20 +383,42 @@ function requireIdentity(value, label) {
|
|
|
380
383
|
}
|
|
381
384
|
return value.trim();
|
|
382
385
|
}
|
|
383
|
-
async function persistAndApply(home, observations,
|
|
386
|
+
async function persistAndApply(home, observations, environment, connection, startupRunId) {
|
|
384
387
|
const inbox = new FileRuntimeEventInbox(home);
|
|
385
|
-
|
|
386
|
-
|
|
388
|
+
const host = {
|
|
389
|
+
protocol: AGENT_HOST_EVENT_PROTOCOL,
|
|
390
|
+
adapterId: requireIdentity(environment.YUI_ADAPTER_ID, "Agent adapter id"),
|
|
391
|
+
workspace: requireIdentity(environment.YUI_WORKSPACE, "Yui workspace"),
|
|
392
|
+
...(connection === undefined ? {} : { connection })
|
|
393
|
+
};
|
|
394
|
+
let events;
|
|
395
|
+
try {
|
|
396
|
+
structuredProviderEventDelivery(home, environment); // prune ACKed local diagnostics
|
|
397
|
+
events = observations.map(entry => inbox.enqueueObservation(entry, {
|
|
398
|
+
...host,
|
|
399
|
+
...(startupRunId === undefined ? {} : { startupRunId })
|
|
400
|
+
}).event);
|
|
401
|
+
for (const event of events)
|
|
402
|
+
emittedEvents.set(event.id, {
|
|
403
|
+
home, taskId: event.taskId, roleName: event.observation.fence.roleName,
|
|
404
|
+
nativeSessionId: event.observation.fence.nativeSessionId,
|
|
405
|
+
terminal: ["turn.completed", "turn.failed", "turn.cancelled"].includes(event.observation.kind)
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
deliveryFailure = deliveryFault("persistence", error);
|
|
410
|
+
throw error;
|
|
411
|
+
}
|
|
387
412
|
// The immutable inbox is authoritative. During a release/update handover the
|
|
388
413
|
// old Controller is draining and the replacement is not ready yet; leave the
|
|
389
414
|
// entries for normal inbox replay instead of turning a healthy Provider Turn
|
|
390
415
|
// into a transport failure.
|
|
391
416
|
if (isForeignHandoverLockHeld(home))
|
|
392
417
|
return;
|
|
393
|
-
for (const entry of
|
|
418
|
+
for (const entry of events) {
|
|
394
419
|
let result;
|
|
395
420
|
try {
|
|
396
|
-
result = await callController(home, "runtime.observation-apply", entry, {
|
|
421
|
+
result = await callController(home, "runtime.host-observation-apply", { eventId: entry.id }, {
|
|
397
422
|
// This is only an eager application hint after durable enqueue, not
|
|
398
423
|
// native admission. Bound it like the wake signal so a frozen
|
|
399
424
|
// Controller cannot serialize ten seconds of delay per tool event.
|
|
@@ -409,7 +434,10 @@ async function persistAndApply(home, observations, taskId, roleName) {
|
|
|
409
434
|
&& ["CONTROLLER_NOT_RUNNING", "CONTROLLER_UNAVAILABLE", "CONTROLLER_TIMEOUT",
|
|
410
435
|
"CONTROLLER_DELIVERY_UNKNOWN"].includes(error.code)))
|
|
411
436
|
return;
|
|
412
|
-
|
|
437
|
+
// The native fact is already durable. An incompatible/refusing
|
|
438
|
+
// Controller is a reporting fault, not Provider execution failure.
|
|
439
|
+
deliveryFailure = deliveryFault("controller", error);
|
|
440
|
+
return;
|
|
413
441
|
}
|
|
414
442
|
// A fast Provider can accept the initial AgentRun before the scheduler call
|
|
415
443
|
// that launched this Host has returned and committed `turn.pushed`. The
|
|
@@ -417,8 +445,43 @@ async function persistAndApply(home, observations, taskId, roleName) {
|
|
|
417
445
|
// `deferred` therefore means "retained for replay", not delivery failure.
|
|
418
446
|
// The signal below schedules the replay after the transport transaction.
|
|
419
447
|
if (result.outcome !== "applied" && result.outcome !== "deferred") {
|
|
420
|
-
|
|
448
|
+
deliveryFailure = deliveryFault("controller", `Structured Provider observation was rejected: ${result.outcome ?? "unknown"}. Event: ${entry.id}`);
|
|
449
|
+
return;
|
|
421
450
|
}
|
|
422
451
|
}
|
|
423
|
-
|
|
452
|
+
if (deliveryFailure?.stage === "controller")
|
|
453
|
+
deliveryFailure = undefined;
|
|
454
|
+
await signalController(home, hostTaskId(environment), requireIdentity(environment.YUI_ROLE, "Role"));
|
|
455
|
+
}
|
|
456
|
+
/** Capture only identities the Host actually received; the current Controller
|
|
457
|
+
* owns all TaskStore reads and resolves the exact attempt's Run at consumption. */
|
|
458
|
+
function describeHostIdentity(environment, adapterId, nativeSessionId) {
|
|
459
|
+
if (environment.YUI_ADAPTER_ID !== adapterId)
|
|
460
|
+
throw new Error("Agent adapter id does not match.");
|
|
461
|
+
return {
|
|
462
|
+
taskId: hostTaskId(environment),
|
|
463
|
+
roleName: requireIdentity(environment.YUI_ROLE, "Role name"),
|
|
464
|
+
agentId: requireIdentity(environment.YUI_AGENT_ID, "Agent id"),
|
|
465
|
+
nativeSessionId: requireIdentity(nativeSessionId, "Native Session id")
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
function hostTaskId(environment) {
|
|
469
|
+
if (environment.YUI_SESSION_SCOPE === "global") {
|
|
470
|
+
if (environment.YUI_TASK_ID !== undefined)
|
|
471
|
+
throw new Error("Global Host must not carry a Task id.");
|
|
472
|
+
return undefined;
|
|
473
|
+
}
|
|
474
|
+
if (environment.YUI_SESSION_SCOPE !== "task")
|
|
475
|
+
throw new Error("Agent Host observation requires Task scope.");
|
|
476
|
+
return requireIdentity(environment.YUI_TASK_ID, "Task id");
|
|
477
|
+
}
|
|
478
|
+
export async function publishStructuredProviderConnection(input) {
|
|
479
|
+
const adapterId = requireIdentity(input.environment.YUI_ADAPTER_ID, "Agent adapter id");
|
|
480
|
+
const fence = describeHostIdentity(input.environment, adapterId, input.nativeSessionId);
|
|
481
|
+
await persistAndApply(input.home, [observation({
|
|
482
|
+
kind: "host.observed", observedAt: new Date().toISOString(),
|
|
483
|
+
authority: "host", payload: { alive: true },
|
|
484
|
+
sequence: nextStructuredSequence(), ordinal: 0,
|
|
485
|
+
fence: { ...fence, driverId: builtinAgentDriverRegistry().requireByAdapterId(adapterId).id }
|
|
486
|
+
})], input.environment, input.connection, input.startupRunId);
|
|
424
487
|
}
|
|
@@ -9,6 +9,11 @@ const LEGACY_ROLE_RUN_DISPATCH_REASONS = new Set([
|
|
|
9
9
|
/** Atomically useful when called inside the caller's TaskStore transaction. */
|
|
10
10
|
export function enqueueWork(store, target, reason, occurredAt, refs = [], metadata = {}) {
|
|
11
11
|
const mailbox = store.getWorkMailbox(target) ?? createWorkMailbox(target);
|
|
12
|
+
// Archive is an admission boundary, not an acknowledgement of old input.
|
|
13
|
+
// Runtime cleanup mailboxes remain usable for exact-owner recovery.
|
|
14
|
+
if ((target.kind === "task" || target.kind === "role")
|
|
15
|
+
&& store.getTask?.(target.taskId)?.status === "archived")
|
|
16
|
+
return mailbox;
|
|
12
17
|
const queued = enqueueSignal(mailbox, {
|
|
13
18
|
reason,
|
|
14
19
|
refs,
|
|
@@ -278,7 +278,7 @@ function projectNextAction(item, lanes, synthesis, mainRun, candidate) {
|
|
|
278
278
|
return action("inspect-unknown", ["leader"], mainRun.runId === undefined ? [item.id] : [mainRun.runId]);
|
|
279
279
|
}
|
|
280
280
|
if (item.status === "open") {
|
|
281
|
-
return action("dispatch-work", ["leader"], [item.id]);
|
|
281
|
+
return action(item.assignee === undefined ? "execute-directly" : "dispatch-work", ["leader"], [item.id]);
|
|
282
282
|
}
|
|
283
283
|
return action("inspect-unknown", ["leader"], [item.id]);
|
|
284
284
|
}
|
|
@@ -342,6 +342,42 @@ export function updateTaskRoleProviderRuntime(set, binding, updatedAt) {
|
|
|
342
342
|
updatedAt: requireDate(updatedAt, "Provider Runtime Binding timestamp")
|
|
343
343
|
});
|
|
344
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* Bind a Global Role's Provider Runtime evidence, mirroring the Task Role
|
|
347
|
+
* writer (decision-3 §6/§9). Create-only: an existing distinct binding is a
|
|
348
|
+
* conflict, an identical one is idempotent. The Global set stays schemaVersion
|
|
349
|
+
* 5; only the optional providerBinding is added.
|
|
350
|
+
*/
|
|
351
|
+
export function bindGlobalRoleProviderRuntime(set, binding, updatedAt) {
|
|
352
|
+
validateRoleSessionSet(set);
|
|
353
|
+
const normalized = validateProviderRuntimeBinding(binding);
|
|
354
|
+
if (set.providerBinding != null) {
|
|
355
|
+
if (JSON.stringify(set.providerBinding) === JSON.stringify(normalized))
|
|
356
|
+
return set;
|
|
357
|
+
throw new Error("Global Role already has a Provider Runtime Binding.");
|
|
358
|
+
}
|
|
359
|
+
return validateRoleSessionSet({
|
|
360
|
+
...set,
|
|
361
|
+
providerBinding: normalized,
|
|
362
|
+
updatedAt: requireDate(updatedAt, "Provider Runtime Binding timestamp")
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
/** In-place update of a Global Role's Provider Runtime binding; namespace and
|
|
366
|
+
* account scope are immutable, exactly as for a Task Role. */
|
|
367
|
+
export function updateGlobalRoleProviderRuntime(set, binding, updatedAt) {
|
|
368
|
+
validateRoleSessionSet(set);
|
|
369
|
+
const normalized = validateProviderRuntimeBinding(binding);
|
|
370
|
+
if (set.providerBinding == null
|
|
371
|
+
|| normalized.providerNamespace !== set.providerBinding.providerNamespace
|
|
372
|
+
|| normalized.accountScope !== set.providerBinding.accountScope) {
|
|
373
|
+
throw new Error("Provider Runtime Binding identity cannot change in place.");
|
|
374
|
+
}
|
|
375
|
+
return validateRoleSessionSet({
|
|
376
|
+
...set,
|
|
377
|
+
providerBinding: normalized,
|
|
378
|
+
updatedAt: requireDate(updatedAt, "Provider Runtime Binding timestamp")
|
|
379
|
+
});
|
|
380
|
+
}
|
|
345
381
|
/**
|
|
346
382
|
* Detaches a confirmed-dead local Host without ending its resumable native
|
|
347
383
|
* Session. Host connections are disposable execution
|
|
@@ -409,7 +445,7 @@ export function recordTaskRoleNativeTurnBoundary(set, input, completedAt) {
|
|
|
409
445
|
export function validateRoleSessionSet(set) {
|
|
410
446
|
const ownerScope = set.owner?.scope;
|
|
411
447
|
rejectUnknownFields(set, ownerScope === "global"
|
|
412
|
-
? ["schemaVersion", "owner", "activeAgentId", "sessions", "updatedAt", "history"]
|
|
448
|
+
? ["schemaVersion", "owner", "activeAgentId", "sessions", "updatedAt", "history", "providerBinding", "interrupts"]
|
|
413
449
|
: [
|
|
414
450
|
"schemaVersion",
|
|
415
451
|
"owner",
|
|
@@ -428,10 +464,19 @@ export function validateRoleSessionSet(set) {
|
|
|
428
464
|
if (set.schemaVersion !== 5) {
|
|
429
465
|
throw new Error("Global Role session set schema version is invalid.");
|
|
430
466
|
}
|
|
431
|
-
|
|
432
|
-
|
|
467
|
+
const globalSet = set;
|
|
468
|
+
for (const [requestId, control] of Object.entries(globalSet.interrupts ?? {})) {
|
|
469
|
+
requireSafeIdentity(requestId, "Global interrupt request");
|
|
470
|
+
requireText(control.fingerprint, "Global interrupt fingerprint");
|
|
471
|
+
requireText(control.attemptId, "Global interrupt attempt");
|
|
472
|
+
requireText(control.nativeSessionId, "Global interrupt Session");
|
|
473
|
+
requireText(control.receiptId, "Global interrupt receipt");
|
|
474
|
+
if (control.receipt !== undefined
|
|
475
|
+
&& !["interrupt-requested", "interrupt-not-active", "interrupt-unknown", "interrupt-unavailable"]
|
|
476
|
+
.includes(control.receipt.state))
|
|
477
|
+
throw new Error("Global interrupt receipt is invalid.");
|
|
433
478
|
}
|
|
434
|
-
const history =
|
|
479
|
+
const history = globalSet.history;
|
|
435
480
|
if (history !== undefined) {
|
|
436
481
|
for (const [ref, session] of Object.entries(history)) {
|
|
437
482
|
requireSafeIdentity(ref, "Operator session ref");
|
|
@@ -441,6 +486,21 @@ export function validateRoleSessionSet(set) {
|
|
|
441
486
|
}
|
|
442
487
|
}
|
|
443
488
|
}
|
|
489
|
+
// A Global Role now carries the same optional Provider Runtime Binding shape
|
|
490
|
+
// as a Task Role (decision-3 §6/§9). It stays absent on every legacy set;
|
|
491
|
+
// when present, it is the exact native control evidence a Global steer or
|
|
492
|
+
// interrupt targets, validated identically to the Task branch. A fabricated
|
|
493
|
+
// Task binding is never accepted here, and its presence never upgrades the
|
|
494
|
+
// schemaVersion these raw-JSON sets are read at.
|
|
495
|
+
if (Object.hasOwn(globalSet, "providerBinding") && globalSet.providerBinding != null) {
|
|
496
|
+
const providerBinding = validateProviderRuntimeBinding(globalSet.providerBinding);
|
|
497
|
+
const conversationId = currentProviderConversation(providerBinding).conversationId;
|
|
498
|
+
const session = [...Object.values(globalSet.sessions), ...Object.values(globalSet.history ?? {})]
|
|
499
|
+
.find(entry => entry.agentId === providerBinding.accountScope && entry.nativeSessionId === conversationId);
|
|
500
|
+
if (session !== undefined && providerBinding.providerNamespace !== builtinDriverIdForAdapter(session.adapterId)) {
|
|
501
|
+
throw new Error("Provider Runtime Binding namespace does not match the Agent adapter.");
|
|
502
|
+
}
|
|
503
|
+
}
|
|
444
504
|
}
|
|
445
505
|
else {
|
|
446
506
|
if (set.schemaVersion !== 12) {
|
|
@@ -177,6 +177,9 @@ export class ExecutorRegistry {
|
|
|
177
177
|
return deliveryReport(outcome);
|
|
178
178
|
}
|
|
179
179
|
async notifyOperatorInputOnce(input) {
|
|
180
|
+
if (this.runtimePorts?.notifyOperatorInputOnce !== undefined) {
|
|
181
|
+
return this.runtimePorts.notifyOperatorInputOnce(input);
|
|
182
|
+
}
|
|
180
183
|
const probe = this.readiness(input.adapterId, "operator");
|
|
181
184
|
return this.tmux.sendRoleInputOnceIfReadyAsync === undefined
|
|
182
185
|
? this.tmux.sendRoleInputOnceIfReady("operator", input.roleName, input.receiptId, input.text, probe)
|