@zq-silk/yui 2.3.0 → 2.5.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 +15 -1
- package/dist/artifacts/artifactCapability.js +39 -10
- package/dist/artifacts/imageMetadata.js +120 -0
- package/dist/artifacts/managedGit.js +7 -4
- package/dist/artifacts/taskArtifactRepository.js +6 -0
- package/dist/cli/commandCatalog.js +18 -9
- package/dist/cli/invocationAuthority.js +1 -1
- package/dist/commands/executionAuditCommands.js +5 -0
- package/dist/commands/globalRoleCommands.js +41 -3
- package/dist/commands/projectCommands.js +89 -94
- package/dist/commands/roleSkillValidation.js +4 -8
- package/dist/commands/taskCommands.js +65 -14
- package/dist/commands/taskUsageCommand.js +38 -0
- package/dist/context/roleSessionContext.js +18 -2
- package/dist/context/runContextPack.js +5 -1
- package/dist/context/sessionBootstrapManifest.js +18 -3
- package/dist/context/skillCatalog.js +108 -0
- package/dist/context/skillPackage.js +161 -0
- package/dist/context/taskContext.js +3 -2
- package/dist/context/taskSearch.js +127 -0
- package/dist/controlPlaneCli.js +25 -20
- package/dist/controller/fileSchedulerStoreAdapter.js +39 -8
- package/dist/controller/globalInputDelivery.js +2 -4
- package/dist/controller/jobControl.js +2 -0
- package/dist/controller/nativeHostInput.js +37 -0
- package/dist/controller/providerRetryDelivery.js +4 -2
- package/dist/controller/runtime.js +93 -2
- package/dist/controller/runtimeEventInbox.js +2 -2
- package/dist/controller/runtimeEventProcessor.js +3 -3
- package/dist/doctor/doctor.js +10 -0
- package/dist/execution/reviewMainRun.js +1 -0
- package/dist/execution/workItemMainRun.js +1 -0
- package/dist/executor/agentAdapter.js +3 -5
- package/dist/executor/codexConfigConflict.js +6 -4
- package/dist/executor/effectiveLaunch.js +24 -1
- package/dist/executor/fileRoleLaunchPlanner.js +15 -7
- package/dist/integration/gitIntegrationService.js +7 -12
- package/dist/integration/integrationSourceApplication.js +4 -2
- package/dist/interaction/notificationPresentation.js +85 -0
- package/dist/interaction/operatorPresentation.js +1 -2
- package/dist/job/httpPreview.js +61 -0
- package/dist/job/jobRunner.js +12 -2
- package/dist/kernel/builtinCapabilities.js +3 -1
- package/dist/message/message.js +15 -1
- package/dist/output/rolePresentation.js +4 -1
- package/dist/repository/executionLaneGitSnapshot.js +2 -1
- package/dist/repository/gitCompatibility.js +62 -0
- package/dist/repository/gitWorkspace.js +48 -71
- package/dist/repository/gitWorktreeInventory.js +121 -0
- package/dist/repository/project.js +74 -25
- package/dist/resources/resourceDiscovery.js +2 -2
- package/dist/resources/resourceGc.js +7 -4
- package/dist/resources/resourceRegistrar.js +2 -1
- package/dist/runtime/agentEndpoint.js +8 -0
- package/dist/runtime/agentHost.js +170 -3
- package/dist/runtime/codexAppServerRuntime.js +1 -1
- package/dist/runtime/codexNativeAccess.js +175 -0
- package/dist/runtime/controlledCodexTui.js +116 -0
- package/dist/runtime/publicReply.js +23 -0
- package/dist/runtime/runtimeObservation.js +48 -0
- package/dist/runtime/structuredProviderHost.js +16 -7
- package/dist/runtime/taskCostMetrics.js +133 -0
- package/dist/runtime/taskUsageMetrics.js +11 -2
- package/dist/runtime/taskUsageQuery.js +38 -0
- package/dist/scheduler/leaderWakeupProcessor.js +1 -9
- package/dist/scheduler/taskExecutionProjection.js +2 -0
- package/dist/scheduler/taskObservabilityProjection.js +9 -1
- package/dist/storage/homeLayout.js +13 -0
- package/dist/storage/sqliteSchema.js +44 -0
- package/dist/storage/sqliteStore.js +53 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +2 -0
- package/dist/storage/taskSearch.js +35 -0
- package/dist/storage/upgrade/projectKnowledgeMigration.js +52 -0
- package/dist/task/ordinaryArchive.js +42 -0
- package/dist/tmux/tmuxManager.js +8 -8
- package/dist/web/assets/assetManifest.js +11 -51
- package/dist/web/assets/client/app/catalog.js +215 -0
- package/dist/web/assets/client/app/main.js +148 -0
- package/dist/web/assets/client/app/selection.js +139 -0
- package/dist/web/assets/client/app/settings.js +326 -0
- package/dist/web/assets/client/app/taskView.js +280 -0
- package/dist/web/assets/client/domain/context.js +96 -0
- package/dist/web/assets/client/domain/records.js +126 -0
- package/dist/web/assets/client/domain/runs.js +119 -0
- package/dist/web/assets/client/domain/taskForms.js +197 -0
- package/dist/web/assets/client/domain/vocab.js +69 -0
- package/dist/web/assets/client/domain/work.js +116 -0
- package/dist/web/assets/client/i18n/en.js +900 -0
- package/dist/web/assets/client/i18n/zh-CN.js +898 -0
- package/dist/web/assets/client/index.js +114 -0
- package/dist/web/assets/client/layout/resizer.js +37 -0
- package/dist/web/assets/client/layout/router.js +35 -0
- package/dist/web/assets/client/layout/shortcuts.js +45 -0
- package/dist/web/assets/client/layout/sizing.js +95 -0
- package/dist/web/assets/client/layout/workspace.js +247 -0
- package/dist/web/assets/client/{api.js → lib/api.js} +20 -4
- package/dist/web/assets/client/{dom.js → lib/dom.js} +24 -12
- package/dist/web/assets/client/{format.js → lib/format.js} +7 -0
- package/dist/web/assets/client/lib/i18n.js +54 -0
- package/dist/web/assets/client/lib/markdown.js +167 -0
- package/dist/web/assets/client/lib/prefs.js +28 -0
- package/dist/web/assets/client/lib/theme.js +68 -0
- package/dist/web/assets/client/ui/containers.js +101 -0
- package/dist/web/assets/client/ui/controls.js +51 -0
- package/dist/web/assets/client/ui/forms.js +48 -0
- package/dist/web/assets/client/ui/metrics.js +137 -0
- package/dist/web/assets/client/ui/primitives.js +92 -0
- package/dist/web/assets/client/ui/text.js +33 -0
- package/dist/web/assets/client/ui/toast.js +12 -0
- package/dist/web/assets/client/views/bodySearch.js +172 -0
- package/dist/web/assets/client/views/createTask.js +85 -0
- package/dist/web/assets/client/views/dock/conversation.js +612 -0
- package/dist/web/assets/client/views/dock/discussion.js +173 -0
- package/dist/web/assets/client/views/dock/preview.js +129 -0
- package/dist/web/assets/client/views/dock/terminal.js +199 -0
- package/dist/web/assets/client/views/globalInput.js +67 -0
- package/dist/web/assets/client/{overview.js → views/overview.js} +61 -51
- package/dist/web/assets/client/{sidebar.js → views/sidebar.js} +56 -62
- package/dist/web/assets/client/views/skills.js +152 -0
- package/dist/web/assets/client/views/task/actions.js +36 -0
- package/dist/web/assets/client/views/task/delivery.js +185 -0
- package/dist/web/assets/client/views/task/deliveryResults.js +159 -0
- package/dist/web/assets/client/views/task/files.js +272 -0
- package/dist/web/assets/client/views/task/observation.js +153 -0
- package/dist/web/assets/client/views/task/overview.js +161 -0
- package/dist/web/assets/client/views/task/page.js +131 -0
- package/dist/web/assets/client/views/task/records.js +163 -0
- package/dist/web/assets/client/views/task/runtime.js +130 -0
- package/dist/web/assets/client/views/task/timeline.js +190 -0
- package/dist/web/assets/shared/geometry.js +19 -0
- package/dist/web/assets/shared/taskTabs.js +8 -0
- package/dist/web/assets/shared/themes.js +93 -0
- package/dist/web/assets/shell/dialogs.js +63 -0
- package/dist/web/assets/shell/dock.js +40 -0
- package/dist/web/assets/shell/index.js +40 -0
- package/dist/web/assets/shell/settings.js +34 -0
- package/dist/web/assets/shell/sidebar.js +45 -0
- package/dist/web/assets/styles/index.js +41 -0
- package/dist/web/assets/styles/{responsive.js → layout/responsive.js} +10 -10
- package/dist/web/assets/styles/{layout.js → layout/workspace.js} +17 -24
- package/dist/web/assets/styles/markdown.js +22 -3
- package/dist/web/assets/styles/tokens.js +10 -63
- package/dist/web/assets/styles/ui/controls.js +76 -0
- package/dist/web/assets/styles/ui/forms.js +46 -0
- package/dist/web/assets/styles/ui/surfaces.js +122 -0
- package/dist/web/assets/styles/views/delivery.js +29 -0
- package/dist/web/assets/styles/views/dock.js +71 -0
- package/dist/web/assets/styles/views/page.js +32 -0
- package/dist/web/assets/styles/views/records.js +70 -0
- package/dist/web/assets/styles/views/settings.js +26 -0
- package/dist/web/assets/styles/views/sidebar.js +74 -0
- package/dist/web/assets/styles/views/task.js +80 -0
- package/dist/web/assets/styles/views/terminal.js +38 -0
- package/dist/web/taskPreviews.js +205 -0
- package/dist/web/tmuxWebTerminal.js +16 -9
- package/dist/web/webConversation.js +373 -0
- package/dist/web/webMaterials.js +155 -0
- package/dist/web/webServer.js +295 -18
- package/dist/web/webSettings.js +331 -0
- package/dist/web/webSkills.js +86 -0
- package/dist/web/webSnapshot.js +40 -8
- package/dist/web/webTaskSurface.js +106 -8
- package/dist/workspace/gitChangeSetCapture.js +3 -1
- package/docs/cli-information-contract.md +1 -1
- package/docs/cli-information-contract.zh-CN.md +1 -1
- package/docs/managed-turn-and-session-runtime.md +31 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +22 -0
- package/docs/release-workflow.md +54 -0
- package/docs/release-workflow.zh-CN.md +39 -0
- package/docs/storage-baseline.md +38 -1
- package/docs/storage-baseline.zh-CN.md +22 -0
- package/docs/task-discovery.md +28 -0
- package/docs/testing/verification-levels.md +2 -2
- package/i18n/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +5 -4
- package/skills/yui-leader/references/execution.md +2 -1
- package/skills/yui-operator/SKILL.md +3 -0
- package/skills/yui-reviewer/SKILL.md +4 -2
- package/skills/yui-runtime/SKILL.md +43 -2
- package/skills/yui-worker/SKILL.md +3 -2
- package/dist/web/assets/client/app.js +0 -808
- package/dist/web/assets/client/components.js +0 -477
- package/dist/web/assets/client/detail.js +0 -379
- package/dist/web/assets/client/dock.js +0 -309
- package/dist/web/assets/client/evidence.js +0 -364
- package/dist/web/assets/client/forms.js +0 -230
- package/dist/web/assets/client/i18n.js +0 -1277
- package/dist/web/assets/client/markdown.js +0 -114
- package/dist/web/assets/client/records.js +0 -198
- package/dist/web/assets/client/sections.js +0 -349
- package/dist/web/assets/client/theme.js +0 -71
- package/dist/web/assets/shell.js +0 -161
- package/dist/web/assets/styles/components.js +0 -216
- package/dist/web/assets/styles/views.js +0 -309
- /package/dist/web/assets/{icons.js → shared/icons.js} +0 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function foldPublicReply(previous, nativeSessionId, method, params) {
|
|
2
|
+
if (params.threadId !== nativeSessionId)
|
|
3
|
+
return previous;
|
|
4
|
+
if (method === "turn/started")
|
|
5
|
+
return undefined;
|
|
6
|
+
if (typeof params.turnId !== "string")
|
|
7
|
+
return previous;
|
|
8
|
+
if (method === "item/agentMessage/delta" && typeof params.itemId === "string" && typeof params.delta === "string") {
|
|
9
|
+
const same = previous?.id === params.itemId && previous.turnId === params.turnId;
|
|
10
|
+
const text = (same ? previous.text : "") + params.delta;
|
|
11
|
+
return { nativeSessionId, turnId: params.turnId, id: params.itemId,
|
|
12
|
+
text: text.slice(0, 6000), truncated: text.length > 6000 || !!(same && previous.truncated) };
|
|
13
|
+
}
|
|
14
|
+
if ((method === "item/started" || method === "item/completed")
|
|
15
|
+
&& params.item !== null && typeof params.item === "object") {
|
|
16
|
+
const item = params.item;
|
|
17
|
+
if (item.type === "agentMessage" && typeof item.id === "string" && typeof item.text === "string") {
|
|
18
|
+
return { nativeSessionId, turnId: params.turnId, id: item.id,
|
|
19
|
+
text: item.text.slice(0, 6000), truncated: item.text.length > 6000 };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return previous;
|
|
23
|
+
}
|
|
@@ -15,6 +15,11 @@ export function isRuntimeTokenEvidence(observation) {
|
|
|
15
15
|
&& (observation.payload.observationQuality === "partial"
|
|
16
16
|
|| observation.payload.observationQuality === "unavailable")));
|
|
17
17
|
}
|
|
18
|
+
/** Both money and tokens survive activity compaction; only tokens feed token metrics. */
|
|
19
|
+
export function isRuntimeUsageEvidence(observation) {
|
|
20
|
+
return isRuntimeTokenEvidence(observation)
|
|
21
|
+
|| (observation.kind === "activity.observed" && observation.payload.cost !== undefined);
|
|
22
|
+
}
|
|
18
23
|
const KINDS = [
|
|
19
24
|
"host.observed",
|
|
20
25
|
"session.started",
|
|
@@ -296,6 +301,11 @@ function normalizePayload(kind, input) {
|
|
|
296
301
|
const observerSource = input.observerSource === undefined
|
|
297
302
|
? undefined
|
|
298
303
|
: normalizeObserverSource(input.observerSource);
|
|
304
|
+
const cost = input.cost === undefined ? undefined : normalizeCost(input.cost);
|
|
305
|
+
if (cost !== undefined && (kind !== "activity.observed"
|
|
306
|
+
|| (cost.semantics === "request" && input.activityId === undefined))) {
|
|
307
|
+
throw new Error("Cost requires activity.observed and a stable request activityId.");
|
|
308
|
+
}
|
|
299
309
|
if (kind === "turn.failed" && input.failure === undefined) {
|
|
300
310
|
throw new Error("run.failed requires normalized failure evidence.");
|
|
301
311
|
}
|
|
@@ -376,6 +386,7 @@ function normalizePayload(kind, input) {
|
|
|
376
386
|
...input.usage,
|
|
377
387
|
semantics: input.usage.semantics ?? "cumulative-session"
|
|
378
388
|
}) }),
|
|
389
|
+
...(cost === undefined ? {} : { cost }),
|
|
379
390
|
...(observerSource === undefined ? {} : { observerSource }),
|
|
380
391
|
...(input.sourceId === undefined
|
|
381
392
|
? {}
|
|
@@ -434,6 +445,43 @@ function normalizePayload(kind, input) {
|
|
|
434
445
|
...(input.goalTokenBudget === undefined ? {} : { goalTokenBudget: input.goalTokenBudget })
|
|
435
446
|
});
|
|
436
447
|
}
|
|
448
|
+
function normalizeCost(input) {
|
|
449
|
+
if (input === null || typeof input !== "object"
|
|
450
|
+
|| !["actual", "estimated"].includes(input.kind)
|
|
451
|
+
|| !["request", "cumulative-session"].includes(input.semantics)) {
|
|
452
|
+
throw new Error("Runtime cost kind or semantics is invalid.");
|
|
453
|
+
}
|
|
454
|
+
if (!Number.isFinite(input.amount) || input.amount < 0 || input.amount > Number.MAX_SAFE_INTEGER) {
|
|
455
|
+
throw new Error("Runtime cost amount must be finite and non-negative.");
|
|
456
|
+
}
|
|
457
|
+
if (typeof input.currency !== "string" || !/^[A-Z]{3}$/u.test(input.currency)) {
|
|
458
|
+
throw new Error("Runtime cost currency requires a three-letter currency code.");
|
|
459
|
+
}
|
|
460
|
+
if (input.kind === "estimated" && input.basis === undefined) {
|
|
461
|
+
throw new Error("Estimated cost requires a price basis.");
|
|
462
|
+
}
|
|
463
|
+
const basis = input.basis;
|
|
464
|
+
if (basis !== undefined && (!Array.isArray(basis.excluded) || basis.excluded.length > 16)) {
|
|
465
|
+
throw new Error("Runtime cost basis excluded items must be a bounded list.");
|
|
466
|
+
}
|
|
467
|
+
return Object.freeze({
|
|
468
|
+
kind: input.kind, semantics: input.semantics, amount: input.amount,
|
|
469
|
+
currency: input.currency, source: costText(input.source, "Cost source"),
|
|
470
|
+
...(basis === undefined ? {} : { basis: Object.freeze({
|
|
471
|
+
model: costText(basis.model, "Cost model"),
|
|
472
|
+
source: costText(basis.source, "Price source"),
|
|
473
|
+
version: costText(basis.version, "Price version or date"),
|
|
474
|
+
scope: costText(basis.scope, "Estimate scope"),
|
|
475
|
+
excluded: Object.freeze(basis.excluded.map(value => costText(value, "Excluded cost", 128)))
|
|
476
|
+
}) })
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
function costText(value, label, limit = 512) {
|
|
480
|
+
const text = requireText(value, label);
|
|
481
|
+
if (text.length > limit)
|
|
482
|
+
throw new Error(`${label} exceeds ${limit} characters.`);
|
|
483
|
+
return text;
|
|
484
|
+
}
|
|
437
485
|
export function runtimeObservationSemanticKey(input) {
|
|
438
486
|
const fence = input.fence;
|
|
439
487
|
const continuationIdentity = [
|
|
@@ -13,6 +13,8 @@ import { unsupportedAgentRunConfiguration } from "./agentRunConfiguration.js";
|
|
|
13
13
|
import { codexAppServerErrorIsMissing, CodexAppServerRequestError, CodexAppServerRuntime, codexClientInitialization, codexGoalNotification, codexTurnInput, codexTurnOutput } from "./codexAppServerRuntime.js";
|
|
14
14
|
import { JsonLineChannel, PROVIDER_MESSAGE_MAX_BYTES, terminateProcessGroup } from "./jsonLineChannel.js";
|
|
15
15
|
import { PROVIDER_ACCEPT_TIMEOUT_MS } from "./runtimeDeadlines.js";
|
|
16
|
+
import { foldPublicReply } from "./publicReply.js";
|
|
17
|
+
import { createCodexNativeAccess } from "./codexNativeAccess.js";
|
|
16
18
|
const CODEX_PROXY_HANDSHAKE_TIMEOUT_MS = 10_000;
|
|
17
19
|
/**
|
|
18
20
|
* These Session implementations do not expose run-configuration observations.
|
|
@@ -297,7 +299,7 @@ class CodexProxyWebSocketChannel {
|
|
|
297
299
|
return;
|
|
298
300
|
}
|
|
299
301
|
const id = requestId(message.id);
|
|
300
|
-
const pending = id === undefined ? undefined : this.#pending.get(id);
|
|
302
|
+
const pending = id === undefined || message.method !== undefined ? undefined : this.#pending.get(id);
|
|
301
303
|
if (pending !== undefined) {
|
|
302
304
|
clearTimeout(pending.timer);
|
|
303
305
|
this.#pending.delete(id);
|
|
@@ -382,6 +384,7 @@ class CodexStructuredProviderSession {
|
|
|
382
384
|
onActivity;
|
|
383
385
|
mirror;
|
|
384
386
|
nativeAccountHome;
|
|
387
|
+
nativeAccess;
|
|
385
388
|
#turnAttempts = new Map();
|
|
386
389
|
adapterId = "codex";
|
|
387
390
|
#activeTurnId;
|
|
@@ -391,6 +394,7 @@ class CodexStructuredProviderSession {
|
|
|
391
394
|
#bufferedStarts = [];
|
|
392
395
|
#bufferedTerminals = [];
|
|
393
396
|
#bufferedActivities = [];
|
|
397
|
+
publicReply;
|
|
394
398
|
constructor(child, exit, processInstanceId, conversationId, runtime, onStarted, onTerminal, onGoal, onInput, onActivity, mirror, nativeAccountHome) {
|
|
395
399
|
this.child = child;
|
|
396
400
|
this.exit = exit;
|
|
@@ -412,13 +416,16 @@ class CodexStructuredProviderSession {
|
|
|
412
416
|
await channel.notify("initialized");
|
|
413
417
|
const runtime = new CodexAppServerRuntime(channel);
|
|
414
418
|
let conversationId;
|
|
419
|
+
let nativeConfiguration;
|
|
415
420
|
let resumedActiveTurnId;
|
|
416
421
|
let resumedTurns = [];
|
|
417
422
|
if (control.mode === "new") {
|
|
418
|
-
|
|
423
|
+
const opened = await runtime.openConversation({
|
|
419
424
|
cwd: payload.cwd,
|
|
420
425
|
...control.codexThread
|
|
421
|
-
})
|
|
426
|
+
});
|
|
427
|
+
conversationId = opened.conversationId;
|
|
428
|
+
nativeConfiguration = opened.raw;
|
|
422
429
|
}
|
|
423
430
|
else {
|
|
424
431
|
try {
|
|
@@ -427,6 +434,7 @@ class CodexStructuredProviderSession {
|
|
|
427
434
|
...control.codexThread
|
|
428
435
|
});
|
|
429
436
|
conversationId = resumed.threadId;
|
|
437
|
+
nativeConfiguration = resumed.raw;
|
|
430
438
|
resumedActiveTurnId = resumed.activeTurnId;
|
|
431
439
|
resumedTurns = resumed.turns;
|
|
432
440
|
}
|
|
@@ -456,6 +464,7 @@ class CodexStructuredProviderSession {
|
|
|
456
464
|
}
|
|
457
465
|
}
|
|
458
466
|
const session = new CodexStructuredProviderSession(child, exit, processInstanceId, conversationId, runtime, onStarted, onTerminal, onGoal, onInput, onActivity, mirror, optionalId(initialized.codexHome));
|
|
467
|
+
session.nativeAccess = createCodexNativeAccess(conversationId, initialized, channel, openingMessages, nativeConfiguration);
|
|
459
468
|
session.#activeTurnId = resumedActiveTurnId;
|
|
460
469
|
const ownedTurn = control.kind === "restore" ? control.ownedTurn : undefined;
|
|
461
470
|
session.#clientOwnedTurnId = ownedTurn?.turnId;
|
|
@@ -514,6 +523,8 @@ class CodexStructuredProviderSession {
|
|
|
514
523
|
const method = typeof message.method === "string" ? message.method : "";
|
|
515
524
|
const params = object(message.params) ?? {};
|
|
516
525
|
const threadId = optionalId(params.threadId);
|
|
526
|
+
if (emit)
|
|
527
|
+
this.publicReply = foldPublicReply(this.publicReply, this.conversationId, method, params);
|
|
517
528
|
if (threadId !== this.conversationId) {
|
|
518
529
|
if (threadId === undefined && (method === "turn/started" || method === "turn/completed")) {
|
|
519
530
|
this.mirror("stderr", `Codex protocol: ${method} has no thread identity; ignored.\n`);
|
|
@@ -630,11 +641,9 @@ class CodexStructuredProviderSession {
|
|
|
630
641
|
get activeTurnId() {
|
|
631
642
|
return this.#activeTurnId;
|
|
632
643
|
}
|
|
633
|
-
/**
|
|
634
|
-
* This implementation has no run-configuration observation reader.
|
|
635
|
-
*/
|
|
644
|
+
/** Last model confirmation from native start/resume, with its observation time. */
|
|
636
645
|
get runConfiguration() {
|
|
637
|
-
return CODEX_RUN_CONFIGURATION;
|
|
646
|
+
return this.nativeAccess?.runConfiguration ?? CODEX_RUN_CONFIGURATION;
|
|
638
647
|
}
|
|
639
648
|
async submitTurn(turn) {
|
|
640
649
|
if (this.#activeTurnId !== undefined) {
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/** No price catalog, currency conversion, token-to-money guess, or billing claim. */
|
|
2
|
+
export function projectTaskCosts(observations, inScope, workItem, missingEvidence = []) {
|
|
3
|
+
const main = observations.filter(o => o.fence.nativeSessionId !== undefined
|
|
4
|
+
&& o.fence.continuationId === undefined && o.fence.parentContinuationId === undefined
|
|
5
|
+
&& (o.authority === "provider-structured" || o.authority === "driver-inferred"));
|
|
6
|
+
const resource = (o) => JSON.stringify([
|
|
7
|
+
o.fence.driverId, o.fence.nativeSessionId
|
|
8
|
+
]);
|
|
9
|
+
const sessions = new Map();
|
|
10
|
+
for (const o of main)
|
|
11
|
+
sessions.set(resource(o), [...(sessions.get(resource(o)) ?? []), o]);
|
|
12
|
+
const project = (kind) => {
|
|
13
|
+
const evidence = [];
|
|
14
|
+
const reasons = [...missingEvidence];
|
|
15
|
+
if (observations.some(o => inScope(o) && o.payload.cost?.kind === kind
|
|
16
|
+
&& (o.fence.continuationId !== undefined || o.fence.parentContinuationId !== undefined))) {
|
|
17
|
+
reasons.push("child-usage-overlap-unproven");
|
|
18
|
+
}
|
|
19
|
+
for (const all of sessions.values()) {
|
|
20
|
+
if (!all.some(inScope))
|
|
21
|
+
continue;
|
|
22
|
+
const costs = all.filter(o => o.payload.cost?.kind === kind);
|
|
23
|
+
if (costs.length === 0) {
|
|
24
|
+
reasons.push("cost-unobserved");
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
// Different native Roles and sources cannot safely claim independent charges.
|
|
28
|
+
const overlap = new Set(all.map(o => JSON.stringify([o.fence.roleName, o.fence.agentId]))).size > 1;
|
|
29
|
+
const latest = costs.at(-1);
|
|
30
|
+
const cost = latest.payload.cost;
|
|
31
|
+
// Missing receipts can carry only a coverage boundary, with no cost.
|
|
32
|
+
const local = qualityReasons(all.filter(o => inScope(o)
|
|
33
|
+
&& (o.payload.cost === undefined || o.payload.cost.kind === kind)));
|
|
34
|
+
const semantics = new Set(costs.map(o => o.payload.cost.semantics));
|
|
35
|
+
if (overlap || semantics.size > 1) {
|
|
36
|
+
evidence.push(detail(latest, null, [...local,
|
|
37
|
+
overlap ? "native-session-role-overlap" : "cost-source-overlap"]));
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (cost.semantics === "cumulative-session") {
|
|
41
|
+
let value = null;
|
|
42
|
+
if (new Set(costs.map(o => JSON.stringify([o.payload.cost.source, o.payload.cost.currency]))).size > 1) {
|
|
43
|
+
local.push("cost-source-overlap");
|
|
44
|
+
}
|
|
45
|
+
else if (workItem)
|
|
46
|
+
local.push("cumulative-not-allocatable-to-work-item");
|
|
47
|
+
else if (costs.some((o, i) => i > 0
|
|
48
|
+
&& o.payload.cost.amount < costs[i - 1].payload.cost.amount)) {
|
|
49
|
+
local.push("cost-counter-rollback");
|
|
50
|
+
}
|
|
51
|
+
else if (new Set(costs.map(o => JSON.stringify(o.payload.cost.basis))).size > 1) {
|
|
52
|
+
local.push("cost-basis-changed");
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const baseline = costs[0].payload.cost.amount;
|
|
56
|
+
if (baseline !== 0)
|
|
57
|
+
local.push("nonzero-session-baseline");
|
|
58
|
+
if (baseline === 0 || costs.length > 1)
|
|
59
|
+
value = cost.amount - baseline;
|
|
60
|
+
}
|
|
61
|
+
evidence.push(detail(latest, value, [...local, ...qualityReasons(costs)]));
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// Currency and price basis can change in a request revision. Resolve
|
|
65
|
+
// identity and attribution over every version before monetary grouping.
|
|
66
|
+
const requests = new Map();
|
|
67
|
+
for (const o of costs) {
|
|
68
|
+
const id = o.payload.activityId;
|
|
69
|
+
requests.set(id, [...(requests.get(id) ?? []), o]);
|
|
70
|
+
}
|
|
71
|
+
const bases = new Map();
|
|
72
|
+
for (const request of requests.values()) {
|
|
73
|
+
if (!request.some(inScope))
|
|
74
|
+
continue;
|
|
75
|
+
const selected = request.at(-1);
|
|
76
|
+
const conflict = new Set(request.map(o => o.payload.cost.source)).size > 1
|
|
77
|
+
? "cost-source-overlap"
|
|
78
|
+
: workItem && (!request.every(inScope) || new Set(request.map(o => o.fence.runId)).size !== 1)
|
|
79
|
+
? "request-run-attribution-conflict" : undefined;
|
|
80
|
+
if (conflict !== undefined) {
|
|
81
|
+
evidence.push(detail(selected, null, [...local, conflict]));
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const selectedCost = selected.payload.cost;
|
|
85
|
+
const key = JSON.stringify([selectedCost.currency, selectedCost.source, selectedCost.basis]);
|
|
86
|
+
bases.set(key, [...(bases.get(key) ?? []), selected]);
|
|
87
|
+
}
|
|
88
|
+
for (const group of bases.values()) {
|
|
89
|
+
const subtotal = group.reduce((sum, o) => sum + o.payload.cost.amount, 0);
|
|
90
|
+
evidence.push(detail(group.at(-1), safeAmount(subtotal), [...local, ...qualityReasons(group)]));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (evidence.length === 0)
|
|
94
|
+
reasons.push(kind === "estimated" ? "price-unavailable" : "cost-unobserved");
|
|
95
|
+
reasons.push(...evidence.flatMap(e => e.amount.reasons));
|
|
96
|
+
const amounts = [...new Set(evidence.map(e => e.currency))].sort().flatMap(currency => {
|
|
97
|
+
const known = evidence.filter(e => e.currency === currency && e.amount.value !== null);
|
|
98
|
+
return known.length === 0 ? [] : [{
|
|
99
|
+
...metric(safeAmount(known.reduce((sum, e) => sum + e.amount.value, 0)), reasons), currency
|
|
100
|
+
}];
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
status: amounts.length === 0 ? "unknown" : reasons.length ? "partial" : "known",
|
|
104
|
+
amounts, reasons: [...new Set(reasons)].sort(), evidence
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
return { scope: "observed-sources-only", actual: project("actual"), estimated: project("estimated") };
|
|
108
|
+
}
|
|
109
|
+
function qualityReasons(observations) {
|
|
110
|
+
return [
|
|
111
|
+
...(observations.some(o => o.payload.observationQuality === "partial"
|
|
112
|
+
|| o.payload.observationQuality === "unavailable") ? ["source-coverage-partial"] : []),
|
|
113
|
+
...(observations.some(o => (o.payload.cost?.basis?.excluded.length ?? 0) > 0)
|
|
114
|
+
? ["estimate-exclusions"] : [])
|
|
115
|
+
];
|
|
116
|
+
}
|
|
117
|
+
function detail(o, value, reasons) {
|
|
118
|
+
const cost = o.payload.cost;
|
|
119
|
+
return {
|
|
120
|
+
nativeSessionId: o.fence.nativeSessionId, roleName: o.fence.roleName,
|
|
121
|
+
agentId: o.fence.agentId, driverId: o.fence.driverId,
|
|
122
|
+
source: cost.source, currency: cost.currency, semantics: cost.semantics,
|
|
123
|
+
...(cost.basis === undefined ? {} : { basis: cost.basis }),
|
|
124
|
+
amount: metric(value, reasons)
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function safeAmount(value) {
|
|
128
|
+
return Number.isFinite(value) && value >= 0 && value <= Number.MAX_SAFE_INTEGER ? value : null;
|
|
129
|
+
}
|
|
130
|
+
function metric(value, reasons) {
|
|
131
|
+
return { value, status: value === null ? "unknown" : reasons.length ? "partial" : "known",
|
|
132
|
+
reasons: [...new Set(reasons)].sort() };
|
|
133
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { projectTaskCosts } from "./taskCostMetrics.js";
|
|
1
2
|
import { builtinDriverIdForAdapter } from "./builtinAgentDrivers.js";
|
|
2
3
|
import { isRuntimeTokenEvidence, runtimeObservationFromTaskEvent } from "./runtimeObservation.js";
|
|
3
4
|
import { projectSessionTokenObservations, unobservedSessionTokenMetrics } from "./sessionTokenMetrics.js";
|
|
@@ -39,7 +40,7 @@ export function projectTaskUsageMetrics(input) {
|
|
|
39
40
|
continue;
|
|
40
41
|
const resource = resourceKey(identity);
|
|
41
42
|
const owners = resourceOwners.get(resource) ?? new Set();
|
|
42
|
-
owners.add(identity.roleName);
|
|
43
|
+
owners.add(JSON.stringify([identity.roleName, identity.agentId]));
|
|
43
44
|
resourceOwners.set(resource, owners);
|
|
44
45
|
}
|
|
45
46
|
const sessions = [...buckets.values()]
|
|
@@ -107,14 +108,21 @@ export function projectTaskUsageMetrics(input) {
|
|
|
107
108
|
}];
|
|
108
109
|
});
|
|
109
110
|
const reasons = [];
|
|
111
|
+
const costReasons = [];
|
|
110
112
|
if (scoped.some((observation) => sessionIdentity(observation) === null))
|
|
111
113
|
reasons.push("session-identity-missing");
|
|
114
|
+
if (reasons.length > 0)
|
|
115
|
+
costReasons.push("session-identity-missing");
|
|
112
116
|
for (const run of runs.values()) {
|
|
113
117
|
if (input.workItemId !== undefined && run.workItemId !== input.workItemId)
|
|
114
118
|
continue;
|
|
115
119
|
if (!observations.some((observation) => exactRun(observation, runs)?.id === run.id
|
|
116
120
|
&& usageAuthority(observation) && isRuntimeTokenEvidence(observation)))
|
|
117
121
|
reasons.push("run-usage-unobserved");
|
|
122
|
+
if (!observations.some((observation) => exactRun(observation, runs)?.id === run.id
|
|
123
|
+
&& usageAuthority(observation) && !child(observation) && observation.payload.cost !== undefined)) {
|
|
124
|
+
costReasons.push("run-cost-unobserved");
|
|
125
|
+
}
|
|
118
126
|
}
|
|
119
127
|
const tokenValues = sessions.flatMap(({ tokens }) => tokens.value === null ? [] : [tokens.value]);
|
|
120
128
|
const sum = safeSum(tokenValues);
|
|
@@ -149,6 +157,7 @@ export function projectTaskUsageMetrics(input) {
|
|
|
149
157
|
elapsedSeconds: input.workItemId === undefined
|
|
150
158
|
? taskElapsed(input.task, events, now) : metric(null, ["work-item-lifecycle-not-task-elapsed"]),
|
|
151
159
|
executionSeconds: executionDuration(scoped, now),
|
|
160
|
+
costs: projectTaskCosts(observations, inScope, input.workItemId !== undefined, costReasons),
|
|
152
161
|
coverage: {
|
|
153
162
|
basis: "task-fenced-observations",
|
|
154
163
|
observedSessions: sessions.length,
|
|
@@ -199,7 +208,7 @@ function identityKey(identity) {
|
|
|
199
208
|
return JSON.stringify([identity.taskId, identity.roleName, identity.agentId, identity.driverId, identity.nativeSessionId]);
|
|
200
209
|
}
|
|
201
210
|
function resourceKey(identity) {
|
|
202
|
-
return JSON.stringify([identity.
|
|
211
|
+
return JSON.stringify([identity.driverId, identity.nativeSessionId]);
|
|
203
212
|
}
|
|
204
213
|
function child({ fence }) {
|
|
205
214
|
return fence.continuationId !== undefined || fence.parentContinuationId !== undefined;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { projectTaskUsageMetrics } from "./taskUsageMetrics.js";
|
|
2
|
+
export const TASK_USAGE_FACT_LIMIT = 2000;
|
|
3
|
+
/** Same bounded, read-only fact query for the Task CLI and Web. */
|
|
4
|
+
export function readTaskUsage(store, taskId, options = {}) {
|
|
5
|
+
const task = store.getTask(taskId);
|
|
6
|
+
if (task === null)
|
|
7
|
+
return null;
|
|
8
|
+
const facts = store.readTaskUsageFacts(taskId);
|
|
9
|
+
return pageTaskUsage(projectTaskUsageMetrics({ task, ...facts, now: options.now }), facts.complete, options);
|
|
10
|
+
}
|
|
11
|
+
export function pageTaskUsage(input, complete = true, options = {}) {
|
|
12
|
+
const offset = options.offset ?? 0, limit = options.limit ?? 0;
|
|
13
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(limit) || limit < 0 || limit > 50) {
|
|
14
|
+
throw new Error("Usage detail offset must be non-negative; limit must be 0..50.");
|
|
15
|
+
}
|
|
16
|
+
const bounded = (metric) => complete ? metric : {
|
|
17
|
+
...metric, status: metric.value === null ? "unknown" : "partial",
|
|
18
|
+
reasons: [...new Set([...metric.reasons, "usage-history-limited"])]
|
|
19
|
+
};
|
|
20
|
+
const cost = (value) => ({
|
|
21
|
+
...value,
|
|
22
|
+
status: complete || value.status === "unknown" ? value.status : "partial",
|
|
23
|
+
reasons: complete ? value.reasons : [...new Set([...value.reasons, "usage-history-limited"])],
|
|
24
|
+
amounts: value.amounts.map(amount => ({ ...bounded(amount), currency: amount.currency })),
|
|
25
|
+
evidence: value.evidence.slice(offset, offset + limit)
|
|
26
|
+
});
|
|
27
|
+
const total = Math.max(input.sessions.length, input.costs.actual.evidence.length, input.costs.estimated.evidence.length);
|
|
28
|
+
return {
|
|
29
|
+
...input, tokens: bounded(input.tokens), toolCalls: bounded(input.toolCalls),
|
|
30
|
+
executionSeconds: bounded(input.executionSeconds),
|
|
31
|
+
costs: { scope: input.costs.scope, actual: cost(input.costs.actual), estimated: cost(input.costs.estimated) },
|
|
32
|
+
sessions: input.sessions.slice(offset, offset + limit),
|
|
33
|
+
details: { offset, limit, sessionTotal: input.sessions.length,
|
|
34
|
+
actualTotal: input.costs.actual.evidence.length, estimatedTotal: input.costs.estimated.evidence.length,
|
|
35
|
+
nextOffset: offset + limit < total ? offset + limit : null },
|
|
36
|
+
history: { complete, limit: TASK_USAGE_FACT_LIMIT }
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -86,15 +86,7 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
|
|
|
86
86
|
attempted = true;
|
|
87
87
|
const outcome = await delivery.sendOnce({
|
|
88
88
|
delivery: ready, receiptId: notification.attemptId, notificationId: notification.wakeId,
|
|
89
|
-
text:
|
|
90
|
-
`Yui Task notification: task=${task.id} wake=${notification.wakeId}.`,
|
|
91
|
-
`Read current context: yui task context ${task.id} --json.`,
|
|
92
|
-
`Read the fixed notification window: yui task wake show ${task.id} ${notification.wakeId}.`,
|
|
93
|
-
"Read referenced result Messages in full before deciding their disposition.",
|
|
94
|
-
"Follow the Leader Skill to advance outstanding Task work from these durable facts, or record the exact decision/authority needed.",
|
|
95
|
-
"This is a notification, not an execution assignment. No separate final report is required.",
|
|
96
|
-
"Reading context is not an acknowledgement that its requirements have been implemented."
|
|
97
|
-
].join("\n")
|
|
89
|
+
text: notification.presentation
|
|
98
90
|
});
|
|
99
91
|
const accepted = outcome.status === "sent" || outcome.status === "already-sent";
|
|
100
92
|
const disposition = accepted ? "accepted"
|
|
@@ -36,6 +36,7 @@ export function buildTaskExecutionProjection(store, taskId, now = new Date()) {
|
|
|
36
36
|
});
|
|
37
37
|
return projectTaskExecution({
|
|
38
38
|
task,
|
|
39
|
+
usageFacts: store.readTaskUsageFacts(taskId),
|
|
39
40
|
roles,
|
|
40
41
|
runs,
|
|
41
42
|
usageRuns,
|
|
@@ -110,6 +111,7 @@ export function projectTaskExecution(facts) {
|
|
|
110
111
|
workItems,
|
|
111
112
|
executionGroups: observabilityGroups,
|
|
112
113
|
runs: facts.usageRuns ?? runs,
|
|
114
|
+
usageFacts: facts.usageFacts,
|
|
113
115
|
events,
|
|
114
116
|
contextSnapshots: facts.contextSnapshots,
|
|
115
117
|
sessionTokens,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { projectTaskUsageMetrics } from "../runtime/taskUsageMetrics.js";
|
|
2
|
+
import { pageTaskUsage } from "../runtime/taskUsageQuery.js";
|
|
2
3
|
/**
|
|
3
4
|
* Build the read-only DAG, execution, cost, and context view consumed by CLI
|
|
4
5
|
* and Web. It deliberately derives every value from existing Task records and
|
|
@@ -145,8 +146,15 @@ function projectCost(input, groups, workItemId) {
|
|
|
145
146
|
const groupIds = new Set(uniqueGroups.map(({ id }) => id));
|
|
146
147
|
const attempts = input.runs.filter(({ executionGroupId }) => (executionGroupId !== undefined && groupIds.has(executionGroupId)));
|
|
147
148
|
const laneCount = uniqueGroups.reduce((total, group) => total + group.lanes.length, 0);
|
|
149
|
+
const usage = projectTaskUsageMetrics({
|
|
150
|
+
...input,
|
|
151
|
+
...(input.usageFacts === undefined ? {} : {
|
|
152
|
+
events: input.usageFacts.events, runs: input.usageFacts.runs
|
|
153
|
+
}),
|
|
154
|
+
workItemId
|
|
155
|
+
});
|
|
148
156
|
return Object.freeze({
|
|
149
|
-
...
|
|
157
|
+
...(input.usageFacts === undefined ? usage : pageTaskUsage(usage, input.usageFacts.complete)),
|
|
150
158
|
laneCount,
|
|
151
159
|
groupCount: uniqueGroups.length,
|
|
152
160
|
retryCount: Math.max(0, attempts.length - laneCount),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
+
import { statSync } from "node:fs";
|
|
3
4
|
/**
|
|
4
5
|
* The single authority for every Yui self-managed path derived from a Home.
|
|
5
6
|
*
|
|
@@ -64,6 +65,18 @@ export function integrationTmuxSocketRoot(home) {
|
|
|
64
65
|
const homeDigest = createHash("sha256").update(homeRoot(home)).digest("hex").slice(0, 16);
|
|
65
66
|
return join("/tmp", `yi-${uid.toString(36)}-${homeDigest}`);
|
|
66
67
|
}
|
|
68
|
+
/** Short IPC endpoint for one foreground HTTP Job. Its durable owner and
|
|
69
|
+
* receipts stay in the Job artifact directory; only the socket lives here. */
|
|
70
|
+
export function jobPreviewSocketRoot(artifactDir) {
|
|
71
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : 0;
|
|
72
|
+
const identity = statSync(artifactDir);
|
|
73
|
+
if (!identity.isDirectory())
|
|
74
|
+
throw new Error("Preview Job artifacts must be a directory.");
|
|
75
|
+
const owner = createHash("sha256").update(JSON.stringify([
|
|
76
|
+
resolve(artifactDir), identity.dev, identity.ino
|
|
77
|
+
])).digest("hex").slice(0, 24);
|
|
78
|
+
return join("/tmp", `yp-${uid.toString(36)}-${owner}`);
|
|
79
|
+
}
|
|
67
80
|
/**
|
|
68
81
|
* Disposable staging prefix for `yui update`'s side-by-side package install
|
|
69
82
|
* (an upgrade artifact): `<home>/runtime/update-staging`. Kept under Home so a
|
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import Database from "better-sqlite3";
|
|
3
3
|
import { BASELINE_SCHEMA_SQL, SQLITE_SCHEMA_TABLES } from "./baselineSchema.js";
|
|
4
4
|
import { migrateTaskMainWorkspaces } from "./upgrade/taskMainWorkspaceMigration.js";
|
|
5
|
+
import { migrateProjectKnowledge } from "./upgrade/projectKnowledgeMigration.js";
|
|
5
6
|
import { CURRENT_STORAGE_VERSION, MIN_SUPPORTED_STORAGE_VERSION, STORAGE_FORMAT, isMinorStorageUpgrade, isStorageVersion, storageVersionParts } from "./storageVersions.js";
|
|
6
7
|
export { BASELINE_SCHEMA_SQL, SQLITE_SCHEMA_TABLES };
|
|
7
8
|
export const TELEMETRY_KEEP_PER_RUN = 200;
|
|
@@ -34,6 +35,47 @@ const MINOR_UPGRADES = Object.freeze([{
|
|
|
34
35
|
// New grants can carry source evidence. Valid historical Operator grants
|
|
35
36
|
// remain unchanged; migration must not invent authorization provenance.
|
|
36
37
|
sql: ""
|
|
38
|
+
}, {
|
|
39
|
+
fromVersion: "1.2",
|
|
40
|
+
toVersion: "1.3",
|
|
41
|
+
name: "selected-session-input",
|
|
42
|
+
introducedIn: "next",
|
|
43
|
+
sourceChecksum: CURRENT_SCHEMA_CHECKSUM,
|
|
44
|
+
targetChecksum: CURRENT_SCHEMA_CHECKSUM,
|
|
45
|
+
// Optional explicit Session fences apply only to new inputs. Preserve old
|
|
46
|
+
// Role-addressed messages; never infer a historical user's Session choice.
|
|
47
|
+
sql: ""
|
|
48
|
+
}, {
|
|
49
|
+
fromVersion: "1.3",
|
|
50
|
+
toVersion: "1.4",
|
|
51
|
+
name: "project-knowledge-applicability",
|
|
52
|
+
introducedIn: "next",
|
|
53
|
+
sourceChecksum: CURRENT_SCHEMA_CHECKSUM,
|
|
54
|
+
targetChecksum: CURRENT_SCHEMA_CHECKSUM,
|
|
55
|
+
sql: "",
|
|
56
|
+
dataMigration: "project-knowledge"
|
|
57
|
+
}, {
|
|
58
|
+
fromVersion: "1.4",
|
|
59
|
+
toVersion: "1.5",
|
|
60
|
+
name: "frozen-skill-packages",
|
|
61
|
+
introducedIn: "next",
|
|
62
|
+
sourceChecksum: CURRENT_SCHEMA_CHECKSUM,
|
|
63
|
+
targetChecksum: CURRENT_SCHEMA_CHECKSUM,
|
|
64
|
+
// Additive effective-launch/manifest package evidence. Historical envelopes
|
|
65
|
+
// and their digests remain unchanged: absence explicitly means entrypoint-only
|
|
66
|
+
// provenance, not an invitation to invent packages from today's source files.
|
|
67
|
+
// Existing Hosts retain their original inputs. Restoring such a Session fails
|
|
68
|
+
// with a bounded new-Session instruction; ordinary readers never backfill it.
|
|
69
|
+
sql: ""
|
|
70
|
+
}, {
|
|
71
|
+
fromVersion: "1.5",
|
|
72
|
+
toVersion: "1.6",
|
|
73
|
+
name: "runtime-cost-evidence",
|
|
74
|
+
introducedIn: "next",
|
|
75
|
+
sourceChecksum: CURRENT_SCHEMA_CHECKSUM,
|
|
76
|
+
targetChecksum: CURRENT_SCHEMA_CHECKSUM,
|
|
77
|
+
// Optional sourced monetary observations; historical missing costs stay unknown.
|
|
78
|
+
sql: ""
|
|
37
79
|
}]);
|
|
38
80
|
export function storageMinorUpgradePlan(from) {
|
|
39
81
|
if (from === CURRENT_STORAGE_VERSION)
|
|
@@ -143,6 +185,8 @@ export function applySqliteMinorUpgrades(db, home, createdRoots = []) {
|
|
|
143
185
|
if (step.dataMigration === "task-main-workspace") {
|
|
144
186
|
migrateTaskMainWorkspaces(db, home, createdRoots);
|
|
145
187
|
}
|
|
188
|
+
if (step.dataMigration === "project-knowledge")
|
|
189
|
+
migrateProjectKnowledge(db);
|
|
146
190
|
const { major, minor } = storageVersionParts(step.toVersion);
|
|
147
191
|
db.prepare("UPDATE storage_schema SET major=?,minor=?,checksum=? WHERE id=1")
|
|
148
192
|
.run(major, minor, step.targetChecksum);
|
|
@@ -66,6 +66,7 @@ import { initializeSqliteSchema, validateSqliteSchema, SqliteSchemaError, TELEME
|
|
|
66
66
|
import { StorageSchemaError } from "./storageSchema.js";
|
|
67
67
|
import { STORAGE_FORMAT } from "./storageVersions.js";
|
|
68
68
|
import { queryTaskCatalog } from "./taskCatalog.js";
|
|
69
|
+
import { queryTaskSearch } from "./taskSearch.js";
|
|
69
70
|
import { CURRENT_CONFIG_SCHEMA_VERSION, CURRENT_WORK_MAILBOX_SCHEMA_VERSION, executionLaneActiveRunKey, executionLaneActiveRunKeyParts, isValidCapabilityGrantTransition, isValidReleaseWorkflowTransition, pendingWakeupProjection, StorageCancelledError, StorageConflictError, StorageRecordError, storedCapabilityGrant, storedPublicationReference, storedReleaseWorkflow, validateYuiConfig } from "./taskStore.js";
|
|
70
71
|
/** Read the immutable Home identity without opening a writable Store connection. */
|
|
71
72
|
export function readSqliteHomeIdentity(rootDir, databaseFilename = "yui.db") {
|
|
@@ -253,6 +254,55 @@ export class SqliteTaskStore {
|
|
|
253
254
|
listEventsByType(taskId, types) {
|
|
254
255
|
return this.#sortById(this.#listPayload("events", "task_id = ? AND type IN (SELECT value FROM json_each(?))", [taskId, JSON.stringify(types)]), event => event.id);
|
|
255
256
|
}
|
|
257
|
+
readTaskUsageFacts(taskId) {
|
|
258
|
+
// Bounded source selection, not a scan/deserialization of Task history.
|
|
259
|
+
// Strip result bodies before they cross the SQLite boundary.
|
|
260
|
+
// Each event type uses idx_events_type_time (including its rowid tie
|
|
261
|
+
// breaker); only bounded prefixes are merged before JSON projection.
|
|
262
|
+
const limit = 2000;
|
|
263
|
+
const rows = this.#db.prepare(`SELECT json_set(payload, '$.payload.observation',
|
|
264
|
+
CASE WHEN type = 'runtime.observation' THEN
|
|
265
|
+
json_remove(json_extract(payload, '$.payload.observation'),
|
|
266
|
+
'$.payload.output', '$.payload.input', '$.payload.failure.lastOutput', '$.payload.summary')
|
|
267
|
+
ELSE NULL END) AS payload
|
|
268
|
+
FROM (
|
|
269
|
+
SELECT * FROM (
|
|
270
|
+
SELECT * FROM (
|
|
271
|
+
SELECT rowid AS source_order, type, occurred_at, payload FROM events
|
|
272
|
+
WHERE task_id = ? AND type = 'runtime.observation'
|
|
273
|
+
ORDER BY occurred_at DESC, rowid DESC LIMIT ?
|
|
274
|
+
)
|
|
275
|
+
UNION ALL
|
|
276
|
+
SELECT * FROM (
|
|
277
|
+
SELECT rowid AS source_order, type, occurred_at, payload FROM events
|
|
278
|
+
WHERE task_id = ? AND type = 'task.cancelled'
|
|
279
|
+
ORDER BY occurred_at DESC, rowid DESC LIMIT ?
|
|
280
|
+
)
|
|
281
|
+
) ORDER BY occurred_at DESC, source_order DESC LIMIT ?
|
|
282
|
+
)`).all(taskId, limit + 1, taskId, limit + 1, limit + 1);
|
|
283
|
+
const events = rows.slice(0, limit).map(row => {
|
|
284
|
+
const event = JSON.parse(row.payload);
|
|
285
|
+
// json_set embeds normalized JSON as an object; the event wire contract
|
|
286
|
+
// stores the canonical observation in a string.
|
|
287
|
+
const observation = event.payload.observation;
|
|
288
|
+
return typeof observation === "object" && observation !== null
|
|
289
|
+
? { ...event, payload: { ...event.payload, observation: JSON.stringify(observation) } } : event;
|
|
290
|
+
});
|
|
291
|
+
// The Task/Run primary key supplies a deterministic bounded ID window,
|
|
292
|
+
// not a "latest by time" claim. Beyond the cap coverage is partial.
|
|
293
|
+
const runs = this.#db.prepare(`SELECT json_object(
|
|
294
|
+
'id', turn_id, 'taskId', task_id, 'roleName', json_extract(payload, '$.roleName'),
|
|
295
|
+
'workItemId', json_extract(payload, '$.workItemId'),
|
|
296
|
+
'effective', json_object('agentId', json_extract(payload, '$.effective.agentId'),
|
|
297
|
+
'adapterId', json_extract(payload, '$.effective.adapterId'))) AS payload
|
|
298
|
+
FROM (
|
|
299
|
+
SELECT turn_id, task_id, payload FROM turns WHERE task_id = ?
|
|
300
|
+
ORDER BY turn_id DESC LIMIT ?
|
|
301
|
+
)`)
|
|
302
|
+
.all(taskId, limit + 1);
|
|
303
|
+
return { events, runs: runs.slice(0, limit).map(row => JSON.parse(row.payload)),
|
|
304
|
+
complete: rows.length <= limit && runs.length <= limit };
|
|
305
|
+
}
|
|
256
306
|
/** Delivery needs original workspace bases, never complete Run reports. */
|
|
257
307
|
listTaskRunWorkspaceBases(taskId) {
|
|
258
308
|
return this.#db.prepare(`SELECT json_object(
|
|
@@ -978,6 +1028,9 @@ export class SqliteTaskStore {
|
|
|
978
1028
|
queryTaskCatalog(query) {
|
|
979
1029
|
return queryTaskCatalog(this.#db, query);
|
|
980
1030
|
}
|
|
1031
|
+
queryTaskSearch(query) {
|
|
1032
|
+
return queryTaskSearch(this.#db, query);
|
|
1033
|
+
}
|
|
981
1034
|
listTaskChoices() {
|
|
982
1035
|
const rows = this.#db.prepare(`SELECT c.task_id AS id, c.status,
|
|
983
1036
|
json_extract(r.payload, '$.title') AS title
|
package/dist/storage/storeRpc.js
CHANGED
|
@@ -53,8 +53,10 @@ const READ_ONLY_STORE_METHODS = new Set([
|
|
|
53
53
|
"readNextActionFacts",
|
|
54
54
|
"readCompletionReadinessFacts",
|
|
55
55
|
"queryContextRecords",
|
|
56
|
+
"queryTaskSearch",
|
|
56
57
|
"latestEventSequence",
|
|
57
58
|
"listEventsByType",
|
|
59
|
+
"readTaskUsageFacts",
|
|
58
60
|
"listTaskRunWorkspaceBases",
|
|
59
61
|
"contextInputReferences",
|
|
60
62
|
"listActiveRuns",
|