@zachwill/pi-orchestrate 0.8.0 → 0.9.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/extension/catalog.ts +70 -75
- package/extension/delivery.ts +49 -7
- package/extension/domain.ts +83 -58
- package/extension/host.ts +321 -31
- package/extension/index.ts +33 -26
- package/extension/presentation.ts +36 -83
- package/extension/runtime.ts +1702 -884
- package/extension/tools.ts +143 -243
- package/extension/tui.ts +50 -0
- package/extension/worker-session.ts +514 -188
- package/extension/worker-settlement.ts +105 -44
- package/package.json +1 -1
- package/extension/scheduler.ts +0 -85
package/extension/catalog.ts
CHANGED
|
@@ -173,105 +173,100 @@ function fieldValue(frontmatter: unknown, field: string): unknown {
|
|
|
173
173
|
return field in frontmatter ? frontmatter[field] : undefined;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
function hasIssuePath(issuePaths: readonly IssuePath[], ...path: readonly PropertyKey[]): boolean {
|
|
177
|
+
return issuePaths.some((entry) => path.every((key, index) => entry.path[index] === key));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function unexpectedFieldsAt(
|
|
181
|
+
issuePaths: readonly IssuePath[],
|
|
182
|
+
parentPath: readonly PropertyKey[],
|
|
183
|
+
): string[] {
|
|
184
|
+
return issuePaths
|
|
185
|
+
.filter(({ path, issue }) =>
|
|
186
|
+
issue._tag === "UnexpectedKey" &&
|
|
187
|
+
path.length === parentPath.length + 1 &&
|
|
188
|
+
parentPath.every((key, index) => path[index] === key) &&
|
|
189
|
+
typeof path[parentPath.length] === "string"
|
|
190
|
+
)
|
|
191
|
+
.map(({ path }) => String(path[parentPath.length]))
|
|
192
|
+
.sort(compareText);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function unknownFieldsDiagnostic(scope: string, fields: readonly string[]): string {
|
|
196
|
+
return `unknown ${scope} field${fields.length === 1 ? "" : "s"}: ${fields.join(", ")}`;
|
|
197
|
+
}
|
|
198
|
+
|
|
176
199
|
function listItems(value: unknown): readonly unknown[] {
|
|
177
200
|
if (typeof value === "string") return value.split(",").map((item) => item.trim());
|
|
178
201
|
return Array.isArray(value) ? value : [];
|
|
179
202
|
}
|
|
180
203
|
|
|
204
|
+
const ORDERED_FRONTMATTER_FIELDS = [
|
|
205
|
+
"name", "description", "model", "thinking", "tools", "skills", "compaction", "lifecycle",
|
|
206
|
+
] as const;
|
|
207
|
+
|
|
208
|
+
const FIXED_FIELD_DIAGNOSTICS: Partial<Record<(typeof ORDERED_FRONTMATTER_FIELDS)[number], string>> = {
|
|
209
|
+
name: "frontmatter field 'name' must be a non-empty string",
|
|
210
|
+
description: "frontmatter field 'description' must be a non-empty string",
|
|
211
|
+
skills: "frontmatter field 'skills' must be a comma string or string array",
|
|
212
|
+
lifecycle: "frontmatter field 'lifecycle' must be 'one-shot' or 'interactive'",
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const COMPACTION_FIELD_DIAGNOSTICS = [
|
|
216
|
+
["enabled", "frontmatter field 'compaction.enabled' must be a boolean"],
|
|
217
|
+
["reserveTokens", "frontmatter field 'compaction.reserveTokens' must be a non-negative integer"],
|
|
218
|
+
["keepRecentTokens", "frontmatter field 'compaction.keepRecentTokens' must be a non-negative integer"],
|
|
219
|
+
] as const;
|
|
220
|
+
|
|
181
221
|
function schemaDiagnostic(issue: SchemaIssue.Issue, frontmatter: unknown): string {
|
|
182
222
|
const issuePaths = collectIssuePaths(issue);
|
|
183
|
-
const
|
|
184
|
-
const frontmatterFields = unexpected
|
|
185
|
-
.filter(({ path }) => path.length === 1 && typeof path[0] === "string")
|
|
186
|
-
.map(({ path }) => String(path[0]))
|
|
187
|
-
.sort(compareText);
|
|
223
|
+
const frontmatterFields = unexpectedFieldsAt(issuePaths, []);
|
|
188
224
|
if (frontmatterFields.length > 0) {
|
|
189
|
-
return
|
|
225
|
+
return unknownFieldsDiagnostic("frontmatter", frontmatterFields);
|
|
190
226
|
}
|
|
227
|
+
if (!isUnknownRecord(frontmatter)) return "frontmatter must be a mapping";
|
|
191
228
|
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
({ path }) => path.length === 2 && path[0] === "compaction" && typeof path[1] === "string",
|
|
195
|
-
)
|
|
196
|
-
.map(({ path }) => String(path[1]))
|
|
197
|
-
.sort(compareText);
|
|
198
|
-
|
|
199
|
-
if (typeof frontmatter !== "object" || frontmatter === null || Array.isArray(frontmatter)) {
|
|
200
|
-
return "frontmatter must be a mapping";
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const orderedFields = [
|
|
204
|
-
"name",
|
|
205
|
-
"description",
|
|
206
|
-
"model",
|
|
207
|
-
"thinking",
|
|
208
|
-
"tools",
|
|
209
|
-
"skills",
|
|
210
|
-
"compaction",
|
|
211
|
-
"lifecycle",
|
|
212
|
-
];
|
|
213
|
-
const field = orderedFields.find((candidate) =>
|
|
214
|
-
issuePaths.some(({ path }) => path[0] === candidate)
|
|
229
|
+
const field = ORDERED_FRONTMATTER_FIELDS.find((candidate) =>
|
|
230
|
+
hasIssuePath(issuePaths, candidate)
|
|
215
231
|
);
|
|
216
|
-
|
|
232
|
+
if (field === undefined) return "invalid worker definition";
|
|
217
233
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
234
|
+
const fixedDiagnostic = FIXED_FIELD_DIAGNOSTICS[field];
|
|
235
|
+
if (fixedDiagnostic !== undefined) return fixedDiagnostic;
|
|
236
|
+
|
|
237
|
+
const value = fieldValue(frontmatter, field);
|
|
221
238
|
if (field === "model") {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
return "frontmatter field 'model' must use provider/model format";
|
|
239
|
+
return typeof value !== "string" || value.trim() === ""
|
|
240
|
+
? "frontmatter field 'model' must be a non-empty string"
|
|
241
|
+
: "frontmatter field 'model' must use provider/model format";
|
|
226
242
|
}
|
|
227
243
|
if (field === "thinking") {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
return `unsupported thinking level '${value.trim()}'`;
|
|
244
|
+
return typeof value !== "string" || value.trim() === ""
|
|
245
|
+
? "frontmatter field 'thinking' must be a non-empty string"
|
|
246
|
+
: `unsupported thinking level '${value.trim()}'`;
|
|
232
247
|
}
|
|
233
248
|
if (field === "tools") {
|
|
234
249
|
const items = listItems(value);
|
|
235
250
|
const validList = items.length > 0 && items.every(
|
|
236
251
|
(item) => typeof item === "string" && item !== "",
|
|
237
252
|
);
|
|
238
|
-
const unsupported = validList
|
|
239
|
-
(item) => typeof item === "string" && !isSupportedToolName(item)
|
|
240
|
-
|
|
253
|
+
const unsupported = validList
|
|
254
|
+
? items.find((item) => typeof item === "string" && !isSupportedToolName(item))
|
|
255
|
+
: undefined;
|
|
241
256
|
if (typeof unsupported === "string") return `unsupported tool '${unsupported}'`;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (field === "skills") {
|
|
246
|
-
return "frontmatter field 'skills' must be a comma string or string array";
|
|
257
|
+
return value === undefined
|
|
258
|
+
? "frontmatter field 'tools' is required"
|
|
259
|
+
: "frontmatter field 'tools' must be a non-empty comma string or string array";
|
|
247
260
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if (issuePaths.some(({ path }) => path[0] === "compaction" && path[1] === "enabled")) {
|
|
253
|
-
return "frontmatter field 'compaction.enabled' must be a boolean";
|
|
254
|
-
}
|
|
255
|
-
if (
|
|
256
|
-
issuePaths.some(({ path }) =>
|
|
257
|
-
path[0] === "compaction" && path[1] === "reserveTokens"
|
|
258
|
-
)
|
|
259
|
-
) {
|
|
260
|
-
return "frontmatter field 'compaction.reserveTokens' must be a non-negative integer";
|
|
261
|
-
}
|
|
262
|
-
if (
|
|
263
|
-
issuePaths.some(({ path }) =>
|
|
264
|
-
path[0] === "compaction" && path[1] === "keepRecentTokens"
|
|
265
|
-
)
|
|
266
|
-
) {
|
|
267
|
-
return "frontmatter field 'compaction.keepRecentTokens' must be a non-negative integer";
|
|
268
|
-
}
|
|
269
|
-
return "frontmatter field 'compaction' must be a mapping";
|
|
270
|
-
}
|
|
271
|
-
if (field === "lifecycle") {
|
|
272
|
-
return "frontmatter field 'lifecycle' must be 'one-shot' or 'interactive'";
|
|
261
|
+
|
|
262
|
+
const compactionFields = unexpectedFieldsAt(issuePaths, ["compaction"]);
|
|
263
|
+
if (compactionFields.length > 0) {
|
|
264
|
+
return unknownFieldsDiagnostic("compaction", compactionFields);
|
|
273
265
|
}
|
|
274
|
-
|
|
266
|
+
const nestedDiagnostic = COMPACTION_FIELD_DIAGNOSTICS.find(([nestedField]) =>
|
|
267
|
+
hasIssuePath(issuePaths, "compaction", nestedField)
|
|
268
|
+
);
|
|
269
|
+
return nestedDiagnostic?.[1] ?? "frontmatter field 'compaction' must be a mapping";
|
|
275
270
|
}
|
|
276
271
|
|
|
277
272
|
function parseWorker(
|
package/extension/delivery.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { Orchestration } from "./runtime.js";
|
|
3
|
+
import type { WorkerSettlement } from "./worker-settlement.js";
|
|
2
4
|
|
|
3
5
|
export const MAX_DELIVERY_MARKDOWN_BYTES = 50 * 1024;
|
|
4
6
|
export const MAX_WORKER_DELIVERY_MARKDOWN_BYTES = 16 * 1024;
|
|
@@ -38,14 +40,33 @@ interface BoundParent {
|
|
|
38
40
|
interface SynthesisGroupState {
|
|
39
41
|
expected: number;
|
|
40
42
|
readonly acceptedEventIds: string[];
|
|
43
|
+
finalEventId?: string;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
export
|
|
46
|
+
export interface DeliveryService {
|
|
47
|
+
bind(binding: ParentBinding): void;
|
|
48
|
+
unbind(ownerSessionId: string, generation: ParentBindingGeneration): void;
|
|
49
|
+
markAgentStarted(ownerSessionId: string, generation: ParentBindingGeneration): void;
|
|
50
|
+
markAgentSettled(ownerSessionId: string, generation: ParentBindingGeneration): void;
|
|
51
|
+
accept(settlement: WorkerSettlement): boolean;
|
|
52
|
+
skipSynthesisGroupMember(
|
|
53
|
+
ownerSessionId: string,
|
|
54
|
+
synthesisGroupId: string,
|
|
55
|
+
synthesisGroupSize: number,
|
|
56
|
+
): void;
|
|
57
|
+
pendingCount(ownerSessionId: string): number;
|
|
58
|
+
clear(): void;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export class Delivery extends Context.Service<Delivery, DeliveryService>()(
|
|
62
|
+
"@zachwill/pi-orchestrate/Delivery",
|
|
63
|
+
) {}
|
|
64
|
+
|
|
65
|
+
export class DeliveryCoordinator implements DeliveryService {
|
|
44
66
|
private readonly boundParents = new Map<string, BoundParent>();
|
|
45
67
|
private readonly pendingSettlements: WorkerSettlement[] = [];
|
|
46
68
|
private readonly flushingOwners = new Set<string>();
|
|
47
69
|
private readonly synthesisGroups = new Map<string, SynthesisGroupState>();
|
|
48
|
-
private readonly finalSynthesisGroupEventIds = new Set<string>();
|
|
49
70
|
private highestAcceptedSequence = 0;
|
|
50
71
|
|
|
51
72
|
bind(binding: ParentBinding): void {
|
|
@@ -115,7 +136,6 @@ export class DeliveryCoordinator {
|
|
|
115
136
|
this.pendingSettlements.length = 0;
|
|
116
137
|
this.flushingOwners.clear();
|
|
117
138
|
this.synthesisGroups.clear();
|
|
118
|
-
this.finalSynthesisGroupEventIds.clear();
|
|
119
139
|
this.highestAcceptedSequence = 0;
|
|
120
140
|
}
|
|
121
141
|
|
|
@@ -240,12 +260,15 @@ export class DeliveryCoordinator {
|
|
|
240
260
|
}
|
|
241
261
|
if (state.acceptedEventIds.length !== state.expected) return;
|
|
242
262
|
const finalEventId = state.acceptedEventIds.at(-1);
|
|
243
|
-
if (finalEventId)
|
|
263
|
+
if (finalEventId) state.finalEventId = finalEventId;
|
|
244
264
|
}
|
|
245
265
|
|
|
246
266
|
private isFinalBoundary(settlement: WorkerSettlement): boolean {
|
|
247
267
|
if (!settlement.synthesisGroupId) return true;
|
|
248
|
-
|
|
268
|
+
const state = this.synthesisGroups.get(
|
|
269
|
+
synthesisGroupKey(settlement.ownerSessionId, settlement.synthesisGroupId),
|
|
270
|
+
);
|
|
271
|
+
return state?.finalEventId === settlement.eventId;
|
|
249
272
|
}
|
|
250
273
|
|
|
251
274
|
private finishSynthesisGroup(settlement: WorkerSettlement): void {
|
|
@@ -253,10 +276,29 @@ export class DeliveryCoordinator {
|
|
|
253
276
|
this.synthesisGroups.delete(
|
|
254
277
|
synthesisGroupKey(settlement.ownerSessionId, settlement.synthesisGroupId),
|
|
255
278
|
);
|
|
256
|
-
this.finalSynthesisGroupEventIds.delete(settlement.eventId);
|
|
257
279
|
}
|
|
258
280
|
}
|
|
259
281
|
|
|
282
|
+
/** Process-scoped direct delivery state and its Orchestration settlement subscription. */
|
|
283
|
+
export const deliveryLayer: Layer.Layer<Delivery, never, Orchestration> = Layer.effect(
|
|
284
|
+
Delivery,
|
|
285
|
+
Effect.gen(function* () {
|
|
286
|
+
const orchestration = yield* Orchestration;
|
|
287
|
+
const coordinator = new DeliveryCoordinator();
|
|
288
|
+
|
|
289
|
+
// Registered first so subscription release runs before delivery state is cleared.
|
|
290
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => coordinator.clear()));
|
|
291
|
+
yield* Effect.acquireRelease(
|
|
292
|
+
Effect.sync(() => orchestration.subscribeSettlement((settlement) => {
|
|
293
|
+
coordinator.accept(settlement);
|
|
294
|
+
})),
|
|
295
|
+
(unsubscribe) => Effect.sync(unsubscribe),
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
return Delivery.of(coordinator);
|
|
299
|
+
}),
|
|
300
|
+
);
|
|
301
|
+
|
|
260
302
|
function synthesisGroupKey(ownerSessionId: string, synthesisGroupId: string): string {
|
|
261
303
|
return `${ownerSessionId}\u0000${synthesisGroupId}`;
|
|
262
304
|
}
|
package/extension/domain.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import { Schema } from "effect";
|
|
2
3
|
|
|
3
4
|
export const SUPPORTED_TOOL_NAMES = [
|
|
4
5
|
"read",
|
|
@@ -88,17 +89,36 @@ function compareWorkersByName(left: WorkerDefinition, right: WorkerDefinition):
|
|
|
88
89
|
return 0;
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
export
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
readonly instructions: string;
|
|
95
|
-
}
|
|
92
|
+
export const MAX_WORKER_TITLE_LENGTH = 200;
|
|
93
|
+
export const MAX_WORKER_INSTRUCTIONS_LENGTH = 100_000;
|
|
94
|
+
export const CANCELLATION_GRACE_MS = 5_000;
|
|
96
95
|
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
const NonBlankString = Schema.String.check(Schema.isPattern(/\S/));
|
|
97
|
+
const WorkerLabel = NonBlankString.check(
|
|
98
|
+
Schema.isMaxLength(MAX_WORKER_TITLE_LENGTH),
|
|
99
|
+
);
|
|
100
|
+
const WorkerInstructions = NonBlankString.check(
|
|
101
|
+
Schema.isMaxLength(MAX_WORKER_INSTRUCTIONS_LENGTH),
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
export const OrchestrateTaskInput = Schema.Struct({
|
|
105
|
+
worker: WorkerLabel,
|
|
106
|
+
title: WorkerLabel,
|
|
107
|
+
instructions: WorkerInstructions,
|
|
108
|
+
});
|
|
109
|
+
export interface OrchestrateTaskInput extends Schema.Schema.Type<typeof OrchestrateTaskInput> {}
|
|
99
110
|
|
|
100
|
-
|
|
101
|
-
export
|
|
111
|
+
/** A validated worker identity. Worker IDs are stable across interactive generations. */
|
|
112
|
+
export const WorkerId = Schema.String.check(
|
|
113
|
+
Schema.isPattern(/^worker-\S+$/),
|
|
114
|
+
).pipe(Schema.brand("WorkerId"));
|
|
115
|
+
export type WorkerId = typeof WorkerId.Type;
|
|
116
|
+
|
|
117
|
+
/** A validated identity for exactly one worker generation. */
|
|
118
|
+
export const RunId = Schema.String.check(
|
|
119
|
+
Schema.isPattern(/^run-\S+$/),
|
|
120
|
+
).pipe(Schema.brand("RunId"));
|
|
121
|
+
export type RunId = typeof RunId.Type;
|
|
102
122
|
|
|
103
123
|
export type WorkerIdFactory = () => WorkerId;
|
|
104
124
|
export type RunIdFactory = () => RunId;
|
|
@@ -111,13 +131,13 @@ export interface OrchestrateIdFactories {
|
|
|
111
131
|
export function createRandomWorkerIdFactory(
|
|
112
132
|
randomId: () => string = defaultRandomId,
|
|
113
133
|
): WorkerIdFactory {
|
|
114
|
-
return () => `worker-${randomId()}`
|
|
134
|
+
return () => WorkerId.make(`worker-${randomId()}`);
|
|
115
135
|
}
|
|
116
136
|
|
|
117
137
|
export function createRandomRunIdFactory(
|
|
118
138
|
randomId: () => string = defaultRandomId,
|
|
119
139
|
): RunIdFactory {
|
|
120
|
-
return () => `run-${randomId()}`
|
|
140
|
+
return () => RunId.make(`run-${randomId()}`);
|
|
121
141
|
}
|
|
122
142
|
|
|
123
143
|
export function createRandomIdFactories(
|
|
@@ -131,12 +151,12 @@ export function createRandomIdFactories(
|
|
|
131
151
|
|
|
132
152
|
export function createSequentialWorkerIdFactory(startAt = 1): WorkerIdFactory {
|
|
133
153
|
let next = startAt;
|
|
134
|
-
return () => `worker-${next++}`
|
|
154
|
+
return () => WorkerId.make(`worker-${next++}`);
|
|
135
155
|
}
|
|
136
156
|
|
|
137
157
|
export function createSequentialRunIdFactory(startAt = 1): RunIdFactory {
|
|
138
158
|
let next = startAt;
|
|
139
|
-
return () => `run-${next++}`
|
|
159
|
+
return () => RunId.make(`run-${next++}`);
|
|
140
160
|
}
|
|
141
161
|
|
|
142
162
|
export function createSequentialIdFactories(startAt = 1): OrchestrateIdFactories {
|
|
@@ -150,15 +170,23 @@ function defaultRandomId(): string {
|
|
|
150
170
|
return globalThis.crypto.randomUUID();
|
|
151
171
|
}
|
|
152
172
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
173
|
+
const NonnegativeFinite = Schema.Finite.check(
|
|
174
|
+
Schema.isGreaterThanOrEqualTo(0),
|
|
175
|
+
);
|
|
176
|
+
const NonnegativeInteger = Schema.Int.check(
|
|
177
|
+
Schema.isGreaterThanOrEqualTo(0),
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
export const WorkerUsage = Schema.Struct({
|
|
181
|
+
input: NonnegativeFinite,
|
|
182
|
+
output: NonnegativeFinite,
|
|
183
|
+
cacheRead: NonnegativeFinite,
|
|
184
|
+
cacheWrite: NonnegativeFinite,
|
|
185
|
+
cost: NonnegativeFinite,
|
|
186
|
+
contextTokens: NonnegativeFinite,
|
|
187
|
+
turns: NonnegativeInteger,
|
|
188
|
+
});
|
|
189
|
+
export interface WorkerUsage extends Schema.Schema.Type<typeof WorkerUsage> {}
|
|
162
190
|
|
|
163
191
|
/** Direction of the most recent message across the worker/model boundary. */
|
|
164
192
|
export type WorkerMessageDirection = "to-model" | "from-model";
|
|
@@ -173,38 +201,39 @@ export const EMPTY_WORKER_USAGE: WorkerUsage = Object.freeze({
|
|
|
173
201
|
turns: 0,
|
|
174
202
|
});
|
|
175
203
|
|
|
176
|
-
export
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
204
|
+
export const WorkerCompletedOutcome = Schema.Struct({
|
|
205
|
+
status: Schema.Literal("completed"),
|
|
206
|
+
assistantText: Schema.String,
|
|
207
|
+
});
|
|
208
|
+
export const WorkerReadyOutcome = Schema.Struct({
|
|
209
|
+
status: Schema.Literal("ready"),
|
|
210
|
+
assistantText: Schema.String,
|
|
211
|
+
});
|
|
212
|
+
export const WorkerFailedOutcome = Schema.Struct({
|
|
213
|
+
status: Schema.Literal("failed"),
|
|
214
|
+
message: Schema.String,
|
|
215
|
+
assistantText: Schema.optionalKey(Schema.String),
|
|
216
|
+
});
|
|
217
|
+
export const WorkerAbortedOutcome = Schema.Struct({
|
|
218
|
+
status: Schema.Literal("aborted"),
|
|
219
|
+
message: Schema.optionalKey(Schema.String),
|
|
220
|
+
assistantText: Schema.optionalKey(Schema.String),
|
|
221
|
+
});
|
|
222
|
+
/** Outcomes emitted in response to a worker generation. */
|
|
223
|
+
export const WorkerResponseOutcome = Schema.Union([
|
|
224
|
+
WorkerCompletedOutcome,
|
|
225
|
+
WorkerReadyOutcome,
|
|
226
|
+
WorkerFailedOutcome,
|
|
227
|
+
WorkerAbortedOutcome,
|
|
228
|
+
]);
|
|
229
|
+
export type WorkerResponseOutcome = typeof WorkerResponseOutcome.Type;
|
|
230
|
+
|
|
231
|
+
/** Domain outcomes include closure, which is not a generation response. */
|
|
232
|
+
export const WorkerOutcome = Schema.Union([
|
|
233
|
+
WorkerResponseOutcome,
|
|
234
|
+
Schema.Struct({ status: Schema.Literal("closed") }),
|
|
235
|
+
]);
|
|
236
|
+
export type WorkerOutcome = typeof WorkerOutcome.Type;
|
|
208
237
|
export type WorkerStatus =
|
|
209
238
|
| "starting"
|
|
210
239
|
| "running"
|
|
@@ -301,7 +330,3 @@ export function transitionWorkerStatus(
|
|
|
301
330
|
|
|
302
331
|
return { ...worker, status, outcome: undefined };
|
|
303
332
|
}
|
|
304
|
-
|
|
305
|
-
export const MAX_WORKER_TITLE_LENGTH = 200;
|
|
306
|
-
export const MAX_WORKER_INSTRUCTIONS_LENGTH = 100_000;
|
|
307
|
-
export const CANCELLATION_GRACE_MS = 5_000;
|