chore: merge develop into hare/develop
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-companion-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043)
|
||||
// Input contract: 0.1.0 (sha256:a0956070b29f49aafac3b19a0fa54626f285bdf01337f26afc4389ff6d23495a)
|
||||
// Options: companion-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/companion-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_companion_api_types -- --check
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -40,7 +40,7 @@ export type TurnResult = "finished" | "paused";
|
||||
|
||||
export type InvokeKind = "user_send" | "notify" | "worker_event" | "system_reminder" | "wakeup";
|
||||
|
||||
export type RunResult = "finished" | "paused" | "limit_reached" | "rolled_back";
|
||||
export type RunResult = "finished" | "paused" | "limit_reached" | "cancelled" | "rolled_back";
|
||||
|
||||
export type ToolResultDisposition = "success" | "error" | "interrupted" | "cancelled" | "outcome_unknown";
|
||||
|
||||
@@ -136,7 +136,13 @@ export type SessionToolAttachment = { media_type: string,
|
||||
*/
|
||||
data_base64: string, };
|
||||
|
||||
export type SessionSnapshotEntryData = { "kind": "user_input", segments: Array<Segment>, } | { "kind": "message", role: SessionMessageRole, content: Array<SessionContentPart>, } | { "kind": "tool_call", call_id: string, name: string, arguments: string, } | { "kind": "tool_result", call_id: string, summary: string, content?: string | null, is_error: boolean, attachments?: Array<SessionToolAttachment>, } | { "kind": "system_item", item_kind: string, content: string, data?: unknown, } | { "kind": "run_error", message: string, };
|
||||
export type RunYieldReason = "compaction";
|
||||
|
||||
export type RunResumeSource = "compaction" | "pause";
|
||||
|
||||
export type RunFailureKind = "engine" | "compaction";
|
||||
|
||||
export type SessionSnapshotEntryData = { "kind": "user_input", segments: Array<Segment>, } | { "kind": "message", role: SessionMessageRole, content: Array<SessionContentPart>, } | { "kind": "tool_call", call_id: string, name: string, arguments: string, } | { "kind": "tool_result", call_id: string, summary: string, content?: string | null, is_error: boolean, attachments?: Array<SessionToolAttachment>, } | { "kind": "system_item", item_kind: string, content: string, data?: unknown, } | { "kind": "run_yielded", reason: RunYieldReason, active_run_turn_count: number, } | { "kind": "run_resumed", source: RunResumeSource, active_run_turn_count: number, } | { "kind": "run_cancelled" } | { "kind": "run_error", message: string, failure?: RunFailureKind | null, };
|
||||
|
||||
export type SessionSnapshotEntry = {
|
||||
/**
|
||||
@@ -147,7 +153,7 @@ entry_id: string,
|
||||
/**
|
||||
* Timestamp copied from the durable log record that commits this entry.
|
||||
*/
|
||||
timestamp: number, provenance: SessionEntryProvenance, derived_from?: Array<string>, } & ({ "kind": "user_input", segments: Array<Segment>, } | { "kind": "message", role: SessionMessageRole, content: Array<SessionContentPart>, } | { "kind": "tool_call", call_id: string, name: string, arguments: string, } | { "kind": "tool_result", call_id: string, summary: string, content?: string | null, is_error: boolean, attachments?: Array<SessionToolAttachment>, } | { "kind": "system_item", item_kind: string, content: string, data?: unknown, } | { "kind": "run_error", message: string, });
|
||||
timestamp: number, provenance: SessionEntryProvenance, derived_from?: Array<string>, } & ({ "kind": "user_input", segments: Array<Segment>, } | { "kind": "message", role: SessionMessageRole, content: Array<SessionContentPart>, } | { "kind": "tool_call", call_id: string, name: string, arguments: string, } | { "kind": "tool_result", call_id: string, summary: string, content?: string | null, is_error: boolean, attachments?: Array<SessionToolAttachment>, } | { "kind": "system_item", item_kind: string, content: string, data?: unknown, } | { "kind": "run_yielded", reason: RunYieldReason, active_run_turn_count: number, } | { "kind": "run_resumed", source: RunResumeSource, active_run_turn_count: number, } | { "kind": "run_cancelled" } | { "kind": "run_error", message: string, failure?: RunFailureKind | null, });
|
||||
|
||||
export type SessionConversationTurn = { turn_id: string, entries: Array<SessionSnapshotEntry>, };
|
||||
|
||||
@@ -279,6 +285,8 @@ export type SubscriptionWorkerWorkdirAttachment = {
|
||||
*/
|
||||
alias: string, repository_key?: string | null, working_directory_id: SubscriptionWorkdirId, };
|
||||
|
||||
export type SubscriptionWorkerJob = { job_id: string, attempt_id: string, purpose: string, };
|
||||
|
||||
export type SubscriptionWorker = { worker_id: SubscriptionWorkerId,
|
||||
/**
|
||||
* Set by the Workspace Server when projecting a Runtime-owned Worker to clients.
|
||||
@@ -307,7 +315,12 @@ worker_state?: WorkerStateSnapshot | null,
|
||||
/**
|
||||
* Runtime catalog lifecycle compatibility projection; not foreground-state authority.
|
||||
*/
|
||||
state: SubscriptionWorkerState, has_running_internal_workers: boolean, workspace_id?: string | null, display_name?: string | null, profile?: string | null, workdir_attachments?: Array<SubscriptionWorkerWorkdirAttachment>, };
|
||||
state: SubscriptionWorkerState, has_running_internal_workers: boolean, workspace_id?: string | null, display_name?: string | null, profile?: string | null,
|
||||
/**
|
||||
* Backend-owned Job binding. Runtime producers leave this unset; the
|
||||
* Workspace projection enriches both snapshots and live updates.
|
||||
*/
|
||||
job?: SubscriptionWorkerJob | null, workdir_attachments?: Array<SubscriptionWorkerWorkdirAttachment>, };
|
||||
|
||||
export type WorkspaceSubscriptionWorkdir = { working_directory_id: SubscriptionWorkdirId, repository_key: string, state: string, };
|
||||
|
||||
@@ -333,7 +346,7 @@ export type SubscriptionFrame = { protocol_version: number, } & ({ "frame": "req
|
||||
|
||||
export type Method = { "method": "submit", "params": { submission_request_id: string, input: Array<Segment>, } } | { "method": "notify", "params": { notification_request_id: string, message: string, } } | { "method": "worker_event", "params": WorkerEvent } | { "method": "list_pending_submissions" } | { "method": "cancel_pending_submission", "params": { submission_id: string, expected_revision: number, } } | { "method": "clear_pending_submissions", "params": { expected_revision: number, } } | { "method": "continue_pending", "params": { expected_revision: number, expected_head_id: string, } } | { "method": "resume", "params": { command: WorkerCommandEnvelope, } } | { "method": "cancel", "params": { command: WorkerCommandEnvelope, } } | { "method": "pause", "params": { command: WorkerCommandEnvelope, } } | { "method": "compact", "params": { command: WorkerCommandEnvelope, } } | { "method": "list_rewind_targets" } | { "method": "rewind_to", "params": { target: RewindTargetId, expected_head_entries: number, } } | { "method": "shutdown", "params": { command: WorkerCommandEnvelope, } } | { "method": "list_completions", "params": { kind: CompletionKind, prefix: string, } } | { "method": "list_workers" } | { "method": "restore_worker", "params": { name: string, } } | { "method": "register_peer", "params": { name: string, } };
|
||||
|
||||
export type Event = { "event": "submission_accepted", "data": { submission_request_id: string, submission_id: string, disposition: SubmissionDisposition, } } | { "event": "submission_rejected", "data": { submission_request_id: string, message: string, } } | { "event": "pending_submissions_changed", "data": { pending: PendingSubmissionsSnapshot, } } | { "event": "user_message", "data": {
|
||||
export type Event = { "event": "submission_accepted", "data": { submission_request_id: string, submission_id: string, disposition: SubmissionDisposition, } } | { "event": "submission_rejected", "data": { submission_request_id: string, message: string, } } | { "event": "notification_accepted", "data": { notification_request_id: string, } } | { "event": "notification_rejected", "data": { notification_request_id: string, message: string, } } | { "event": "pending_submissions_changed", "data": { pending: PendingSubmissionsSnapshot, } } | { "event": "user_message", "data": {
|
||||
/**
|
||||
* Stable durable history identity. Older mixed-version producers omit it.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-repository-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043)
|
||||
// Input contract: 0.1.0 (sha256:a0956070b29f49aafac3b19a0fa54626f285bdf01337f26afc4389ff6d23495a)
|
||||
// Options: repository-operation-schema-closure,openapi-3.1,format:uint64=safe-number,strict-safe-integers,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/repository-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_repository_openapi_types -- --check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-runtime-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043)
|
||||
// Input contract: 0.1.0 (sha256:a0956070b29f49aafac3b19a0fa54626f285bdf01337f26afc4389ff6d23495a)
|
||||
// Options: runtime-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/runtime-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_runtime_api_types -- --check
|
||||
@@ -115,7 +115,7 @@ export type RuntimeWorkerSpawnAcceptanceRequirement = { kind: "socket_ready"; }
|
||||
|
||||
export type RuntimeWorkerSpawnIntent = { kind: "workspace_companion"; } | { kind: "workspace_orchestrator"; } | { kind: "workspace_coding"; } | { kind: "ticket_role"; role: RuntimeTicketWorkerRole; ticket_id: string; };
|
||||
|
||||
export type RuntimeWorkerSpawnRequest = { acceptance: RuntimeWorkerSpawnAcceptanceRequirement; initial_submit?: Array<unknown>; intent: RuntimeWorkerSpawnIntent; profile: RuntimeProfileSelector; requested_worker_name?: string | null; ticket_assignment?: RuntimeWorkerTicketAssignmentRequest | null; workdir_attachment_requests?: Array<RuntimeWorkerSpawnWorkingDirectoryAttachmentRequest>; };
|
||||
export type RuntimeWorkerSpawnRequest = { acceptance: RuntimeWorkerSpawnAcceptanceRequirement; initial_submit?: Array<unknown>; intent: RuntimeWorkerSpawnIntent; profile: RuntimeProfileSelector; requested_worker_name?: string | null; singleton_key?: string | null; ticket_assignment?: RuntimeWorkerTicketAssignmentRequest | null; workdir_attachment_requests?: Array<RuntimeWorkerSpawnWorkingDirectoryAttachmentRequest>; };
|
||||
|
||||
export type RuntimeWorkerSpawnResponse = { acceptance_evidence?: Array<RuntimeWorkerSpawnAcceptanceEvidence>; diagnostics?: Array<Diagnostic>; state: WorkerOperationState; worker?: RuntimeWorkerSummary | null; };
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-ticket-browser-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043)
|
||||
// Input contract: 0.1.0 (sha256:a0956070b29f49aafac3b19a0fa54626f285bdf01337f26afc4389ff6d23495a)
|
||||
// Options: ticket-objective-merge-request-browser-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/ticket-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_ticket_api_types -- --check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-workdir-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:7b06d005d3436758c96fc4cfd462df76460f89620fd1b58fdf05009d96bb9052)
|
||||
// Input contract: 0.1.0 (sha256:a0956070b29f49aafac3b19a0fa54626f285bdf01337f26afc4389ff6d23495a)
|
||||
// Options: workdir-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/workdir-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_workdir_api_types -- --check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-worker-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043)
|
||||
// Input contract: 0.1.0 (sha256:a0956070b29f49aafac3b19a0fa54626f285bdf01337f26afc4389ff6d23495a)
|
||||
// Options: worker-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/worker-launch-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_worker_launch_api_types -- --check
|
||||
@@ -11,7 +11,7 @@ export type BrowserWorkerWorkingDirectorySelection = { alias: string; relative_c
|
||||
|
||||
export type BrowserWorkspaceOrchestratorResponse = { diagnostics: Array<Diagnostic>; disposition: string; online: boolean; worker?: WorkerLaunchWorkerSummary | null; workspace_id: string; };
|
||||
|
||||
export type CreateWorkspaceWorkerRequest = { control_operation_id?: string | null; display_name: string; initial_submit?: Array<Segment>; profile?: string | null; runtime_id: string; ticket_assignment?: CreateWorkspaceWorkerTicketAssignmentRequest | null; workdir_attachments?: Array<BrowserWorkerWorkingDirectorySelection>; };
|
||||
export type CreateWorkspaceWorkerRequest = { control_operation_id?: string | null; display_name: string; initial_submit?: Array<Segment>; profile?: string | null; runtime_id: string; singleton_key?: string | null; ticket_assignment?: CreateWorkspaceWorkerTicketAssignmentRequest | null; workdir_attachments?: Array<BrowserWorkerWorkingDirectorySelection>; };
|
||||
|
||||
export type CreateWorkspaceWorkerTicketAssignmentRequest = { operation_id: string; ticket_id: string; };
|
||||
|
||||
|
||||
@@ -132,12 +132,22 @@ function canonicalSession(logEntries: unknown[]): Event extends {
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "run_yielded":
|
||||
case "run_resumed":
|
||||
case "run_cancelled":
|
||||
entries.push({
|
||||
entry_id: `legacy-test-${sequence++}`,
|
||||
provenance: "legacy_unknown",
|
||||
...entry,
|
||||
});
|
||||
break;
|
||||
case "run_errored":
|
||||
entries.push({
|
||||
entry_id: `legacy-test-${sequence++}`,
|
||||
provenance: "legacy_unknown",
|
||||
kind: "run_error",
|
||||
message: entry["message"] ?? "Worker run failed.",
|
||||
failure: entry["failure"],
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -291,6 +301,138 @@ Deno.test("Worker state events and acknowledgements replace the full state", ()
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("durable compaction failure reconciles live alert and error in both views", () => {
|
||||
const message = "mid-run compaction failed: summary unavailable";
|
||||
const transition = (
|
||||
entryId: string,
|
||||
data: Record<string, unknown>,
|
||||
): Event => ({
|
||||
event: "session_entry_committed",
|
||||
data: {
|
||||
entry: {
|
||||
entry_id: entryId,
|
||||
timestamp: 10,
|
||||
provenance: "legacy_unknown",
|
||||
...data,
|
||||
},
|
||||
},
|
||||
} as Event);
|
||||
const projection = projectConsole([
|
||||
{
|
||||
eventId: "yielded",
|
||||
event: transition("yielded-entry", {
|
||||
kind: "run_yielded",
|
||||
reason: "compaction",
|
||||
active_run_turn_count: 2,
|
||||
}),
|
||||
},
|
||||
{
|
||||
eventId: "alert",
|
||||
event: {
|
||||
event: "alert",
|
||||
data: {
|
||||
level: "error",
|
||||
source: "compactor",
|
||||
message,
|
||||
timestamp_ms: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
eventId: "failed",
|
||||
event: transition("failed-entry", {
|
||||
kind: "run_error",
|
||||
message,
|
||||
failure: "compaction",
|
||||
}),
|
||||
},
|
||||
{
|
||||
eventId: "controller-error",
|
||||
event: {
|
||||
event: "error",
|
||||
data: { code: "internal", message: "summary unavailable" },
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const errors = projection.lines.filter((line) => line.kind === "error");
|
||||
assertEquals(errors.length, 1);
|
||||
assertEquals(errors[0].title, "Compaction failed");
|
||||
assertEquals(errors[0].body, message);
|
||||
assertEquals(errors[0].entryId, "failed-entry");
|
||||
assertEquals(projectConsoleLines(projection.lines, "normal"), errors);
|
||||
assertEquals(projectConsoleLines(projection.lines, "overview"), errors);
|
||||
});
|
||||
|
||||
Deno.test("snapshot restores compaction terminal without reviving transition progress", () => {
|
||||
const projection = projectConsole([{
|
||||
eventId: "snapshot",
|
||||
event: snapshotEvent("/repo", [
|
||||
{
|
||||
kind: "run_yielded",
|
||||
timestamp: 1,
|
||||
reason: "compaction",
|
||||
active_run_turn_count: 2,
|
||||
},
|
||||
{
|
||||
kind: "run_resumed",
|
||||
timestamp: 2,
|
||||
source: "compaction",
|
||||
active_run_turn_count: 2,
|
||||
},
|
||||
{
|
||||
kind: "run_errored",
|
||||
timestamp: 3,
|
||||
interrupted: false,
|
||||
message: "mid-run compaction failed: summary unavailable",
|
||||
failure: "compaction",
|
||||
},
|
||||
]),
|
||||
}]);
|
||||
|
||||
assertEquals(projection.compaction, null);
|
||||
assertEquals(projection.lines.length, 1);
|
||||
assertEquals(projection.lines[0].title, "Compaction failed");
|
||||
assertEquals(projection.lines[0].kind, "error");
|
||||
});
|
||||
|
||||
Deno.test("intentional cancellation stays non-error in live and snapshot projections", () => {
|
||||
const live = projectConsole([
|
||||
{
|
||||
eventId: "cancelled-terminal",
|
||||
event: {
|
||||
event: "session_entry_committed",
|
||||
data: {
|
||||
entry: {
|
||||
entry_id: "cancelled-entry",
|
||||
timestamp: 10,
|
||||
provenance: "legacy_unknown",
|
||||
kind: "run_cancelled",
|
||||
},
|
||||
},
|
||||
} as Event,
|
||||
},
|
||||
{
|
||||
eventId: "cancelled-run-end",
|
||||
event: {
|
||||
event: "run_end",
|
||||
data: { result: "cancelled" },
|
||||
} as Event,
|
||||
},
|
||||
]);
|
||||
assertEquals(live.lines.some((line) => line.kind === "error"), false);
|
||||
|
||||
const restored = projectConsole([{
|
||||
eventId: "snapshot",
|
||||
event: snapshotEvent("/repo", [{
|
||||
kind: "run_cancelled",
|
||||
timestamp: 10,
|
||||
}]),
|
||||
}]);
|
||||
assertEquals(restored.lines.some((line) => line.kind === "error"), false);
|
||||
assertEquals(restored.compaction, null);
|
||||
});
|
||||
|
||||
Deno.test("snapshot replaces a live error with one durable run_errored row", () => {
|
||||
const projector = createConsoleProjector();
|
||||
let projection = projector.append([
|
||||
|
||||
@@ -1002,6 +1002,7 @@ export function applyProtocolEvent(
|
||||
next.usage = usageText(event.data);
|
||||
break;
|
||||
case "error":
|
||||
if (hasDurableRunFailure(next, event.data.message)) break;
|
||||
next.lines.push(
|
||||
line(
|
||||
envelope.eventId,
|
||||
@@ -1264,12 +1265,55 @@ function upsertStatusLine(
|
||||
}
|
||||
}
|
||||
|
||||
function runFailureMessagesMatch(left: string, right: string): boolean {
|
||||
return left.length > 0 && right.length > 0 &&
|
||||
(left === right || left.endsWith(right) || right.endsWith(left));
|
||||
}
|
||||
|
||||
function isDurableRunFailure(line: ConsoleLine): boolean {
|
||||
return line.kind === "error" &&
|
||||
(line.title === "Run error" || line.title === "Compaction failed");
|
||||
}
|
||||
|
||||
function hasDurableRunFailure(
|
||||
projection: ConsoleProjection,
|
||||
message: string,
|
||||
): boolean {
|
||||
return projection.lines.some((line) =>
|
||||
isDurableRunFailure(line) && runFailureMessagesMatch(line.body, message)
|
||||
);
|
||||
}
|
||||
|
||||
function appendDurableRunFailure(
|
||||
projection: ConsoleProjection,
|
||||
eventId: string,
|
||||
message: string,
|
||||
failure: string | undefined,
|
||||
): void {
|
||||
projection.lines = projection.lines.filter((line) => {
|
||||
if (isDurableRunFailure(line)) return true;
|
||||
return !(line.kind === "error" && runFailureMessagesMatch(line.body, message));
|
||||
});
|
||||
projection.lines.push(
|
||||
line(
|
||||
eventId,
|
||||
"error",
|
||||
failure === "compaction" ? "Compaction failed" : "Run error",
|
||||
message,
|
||||
undefined,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function appendAlertLine(
|
||||
projection: ConsoleProjection,
|
||||
eventId: string,
|
||||
alert: Alert,
|
||||
): void {
|
||||
const isError = alert.level === "error";
|
||||
if (isError && hasDurableRunFailure(projection, alert.message)) return;
|
||||
projection.lines.push(
|
||||
line(
|
||||
eventId,
|
||||
@@ -2144,17 +2188,18 @@ function applySessionEntry(
|
||||
applyTaskSystemItem(projection, item);
|
||||
break;
|
||||
}
|
||||
case "run_yielded":
|
||||
case "run_resumed":
|
||||
case "run_cancelled":
|
||||
// Durable logical-Run transitions and intentional cancellation are
|
||||
// replayable lifecycle state, not errors or Console progress rows.
|
||||
break;
|
||||
case "run_error":
|
||||
projection.lines.push(
|
||||
line(
|
||||
eventId,
|
||||
"error",
|
||||
"Run error",
|
||||
stringField(value, "message") ?? "Worker run failed.",
|
||||
undefined,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
appendDurableRunFailure(
|
||||
projection,
|
||||
eventId,
|
||||
stringField(value, "message") ?? "Worker run failed.",
|
||||
stringField(value, "failure"),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -30,6 +30,7 @@ import type {
|
||||
Event as PodProtocolEvent,
|
||||
Method as PodProtocolMethod,
|
||||
Segment as PodProtocolSegment,
|
||||
SubscriptionWorkerJob,
|
||||
WorkerStateSnapshot,
|
||||
} from "$lib/generated/protocol";
|
||||
import type {
|
||||
@@ -88,6 +89,7 @@ export type Worker =
|
||||
display_name: string;
|
||||
tags: string[];
|
||||
worker_state?: WorkerStateSnapshot | null;
|
||||
job?: SubscriptionWorkerJob | null;
|
||||
diagnostics: Diagnostic[];
|
||||
};
|
||||
|
||||
|
||||
@@ -88,6 +88,47 @@ Deno.test('workspace Worker snapshot keeps equal local ids from different Runtim
|
||||
assertEquals([...projection.workers.keys()].sort(), ['runtime-a:1', 'runtime-b:1']);
|
||||
});
|
||||
|
||||
Deno.test('workspace Worker snapshot and live update preserve canonical Job metadata', () => {
|
||||
const projection = createWorkspaceWorkersProjection();
|
||||
const snapshotWorker = worker('runtime-a', 'job-worker', 1);
|
||||
snapshotWorker.job = {
|
||||
job_id: 'check:T-1:r1',
|
||||
attempt_id: 'check:T-1:r1:attempt:1',
|
||||
purpose: 'ticket_item_check',
|
||||
};
|
||||
applyWorkspaceWorkersFrame(projection, {
|
||||
protocol_version: 1,
|
||||
frame: 'response',
|
||||
message: {
|
||||
result: 'subscribed',
|
||||
payload: {
|
||||
request_id: 'request-1',
|
||||
subscription_id: 'subscription-1',
|
||||
selector: { topic: 'workspace_workers' },
|
||||
snapshot_revision: 1,
|
||||
snapshot: { topic: 'workers', data: { workers: [snapshotWorker] } },
|
||||
},
|
||||
},
|
||||
});
|
||||
assertEquals(projection.workers.get('runtime-a:job-worker')?.job, snapshotWorker.job);
|
||||
|
||||
const updated = { ...snapshotWorker, subject_revision: 2 };
|
||||
updated.job = { ...snapshotWorker.job, attempt_id: 'check:T-1:r1:attempt:2' };
|
||||
applyWorkspaceWorkersFrame(projection, {
|
||||
protocol_version: 1,
|
||||
frame: 'event',
|
||||
message: {
|
||||
event: 'event',
|
||||
data: {
|
||||
subscription_id: 'subscription-1',
|
||||
subject_revision: 2,
|
||||
payload: { event: 'worker_upserted', data: { worker: updated } },
|
||||
},
|
||||
},
|
||||
});
|
||||
assertEquals(projection.workers.get('runtime-a:job-worker')?.job, updated.job);
|
||||
});
|
||||
|
||||
Deno.test('workspace Worker reducer ignores stale events and removes composite subject', () => {
|
||||
const projection = createWorkspaceWorkersProjection();
|
||||
projection.workers.set('runtime-a:1', worker('runtime-a', '1', 3));
|
||||
|
||||
@@ -143,6 +143,7 @@ function projectWorker(
|
||||
display_name: displayName,
|
||||
label: displayName,
|
||||
profile: worker.profile ?? null,
|
||||
job: worker.job ?? null,
|
||||
tags: [],
|
||||
workspace: {
|
||||
visibility: "workspace",
|
||||
|
||||
@@ -63,6 +63,19 @@ Deno.test("live runtime workers are sidebar targets and console targets", () =>
|
||||
assertEquals(canOpenWorkerConsole(liveWorker), true);
|
||||
});
|
||||
|
||||
Deno.test("Backend Job Workers are hidden only from the sidebar View", () => {
|
||||
const jobWorker = worker({
|
||||
state: "running",
|
||||
job: {
|
||||
job_id: "check:T-1:r1",
|
||||
attempt_id: "check:T-1:r1:attempt:1",
|
||||
purpose: "ticket_item_check",
|
||||
},
|
||||
});
|
||||
assertEquals(canShowWorkerInSidebar(jobWorker), false);
|
||||
assertEquals(canOpenWorkerConsole(jobWorker), true);
|
||||
});
|
||||
|
||||
Deno.test("sidebar workers sort running then idle then stopped", () => {
|
||||
const workers = [
|
||||
worker({ worker_id: "3", display_name: "Stopped", state: "stopped" }),
|
||||
|
||||
@@ -22,15 +22,15 @@ export function sidebarWorkerActivity(
|
||||
}
|
||||
|
||||
export function canShowWorkerInSidebar(
|
||||
worker: Pick<Worker, 'implementation'>,
|
||||
worker: Pick<Worker, 'implementation' | 'job'>,
|
||||
): boolean {
|
||||
return worker.implementation.kind !== 'backend_worker_registry';
|
||||
return !worker.job && worker.implementation.kind !== 'backend_worker_registry';
|
||||
}
|
||||
|
||||
export function canOpenWorkerConsole(
|
||||
worker: Pick<Worker, 'implementation'>,
|
||||
): boolean {
|
||||
return canShowWorkerInSidebar(worker);
|
||||
return worker.implementation.kind !== 'backend_worker_registry';
|
||||
}
|
||||
|
||||
type SidebarWorkerLink = Pick<Worker, 'resource_key' | 'display_name'>;
|
||||
|
||||
Reference in New Issue
Block a user