@mp-lb/berth 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/berth.js +362 -228
- package/package.json +1 -1
package/dist/berth.js
CHANGED
|
@@ -5995,7 +5995,7 @@ var require_lib = __commonJS({
|
|
|
5995
5995
|
});
|
|
5996
5996
|
|
|
5997
5997
|
// package.json
|
|
5998
|
-
var version = "0.1.
|
|
5998
|
+
var version = "0.1.3";
|
|
5999
5999
|
|
|
6000
6000
|
// src/runAuthCommand.ts
|
|
6001
6001
|
import { spawn } from "node:child_process";
|
|
@@ -22749,9 +22749,6 @@ var runDeliveryCommand = async (args2) => {
|
|
|
22749
22749
|
throw new Error("Use berth delivery start, complete, record or schema.");
|
|
22750
22750
|
};
|
|
22751
22751
|
|
|
22752
|
-
// src/runInstanceCommand.ts
|
|
22753
|
-
import { hostname as hostname4 } from "node:os";
|
|
22754
|
-
|
|
22755
22752
|
// ../../packages/core/src/work/schemas/slugSchema.ts
|
|
22756
22753
|
var slugSchema = external_exports.string().trim().min(1).max(80).regex(
|
|
22757
22754
|
/^[a-z0-9]+(?:-[a-z0-9]+)*$/,
|
|
@@ -22968,8 +22965,7 @@ var prepareInstance = async (value) => {
|
|
|
22968
22965
|
|
|
22969
22966
|
// src/readTaskLaunchContext.ts
|
|
22970
22967
|
import { realpath as realpath4 } from "node:fs/promises";
|
|
22971
|
-
import {
|
|
22972
|
-
import { basename, dirname as dirname3, join as join5 } from "node:path";
|
|
22968
|
+
import { hostname as hostname3 } from "node:os";
|
|
22973
22969
|
|
|
22974
22970
|
// ../../packages/core/src/work/inspectPreparedInstance.ts
|
|
22975
22971
|
import { execFile as execFile3 } from "node:child_process";
|
|
@@ -23066,6 +23062,66 @@ var inspectPreparedInstance = async (value) => {
|
|
|
23066
23062
|
return { directory, repositories };
|
|
23067
23063
|
};
|
|
23068
23064
|
|
|
23065
|
+
// ../../packages/core/src/work/machineIdentity.ts
|
|
23066
|
+
import { randomUUID } from "node:crypto";
|
|
23067
|
+
import {
|
|
23068
|
+
existsSync as existsSync2,
|
|
23069
|
+
linkSync,
|
|
23070
|
+
mkdirSync,
|
|
23071
|
+
readFileSync,
|
|
23072
|
+
unlinkSync,
|
|
23073
|
+
writeFileSync
|
|
23074
|
+
} from "node:fs";
|
|
23075
|
+
import { homedir as homedir5 } from "node:os";
|
|
23076
|
+
import { join as join5 } from "node:path";
|
|
23077
|
+
var identitySchema = external_exports.object({ id: external_exports.uuid() });
|
|
23078
|
+
var machineIdentity = (home = homedir5(), legacyFile) => {
|
|
23079
|
+
const folder = join5(home, ".berth");
|
|
23080
|
+
const path2 = join5(folder, "identity.json");
|
|
23081
|
+
const read2 = () => identitySchema.parse(JSON.parse(readFileSync(path2, "utf8")));
|
|
23082
|
+
if (existsSync2(path2)) return read2();
|
|
23083
|
+
const appData = process.platform === "darwin" ? join5(home, "Library", "Application Support") : process.platform === "win32" ? process.env.APPDATA : join5(home, ".config");
|
|
23084
|
+
const current = appData && join5(appData, "Berth");
|
|
23085
|
+
const legacy = appData && join5(appData, "Factory");
|
|
23086
|
+
const desktopFolder = current && legacy && !existsSync2(current) && existsSync2(legacy) ? legacy : current;
|
|
23087
|
+
const oldFile = legacyFile ?? (desktopFolder ? join5(desktopFolder, "machine-id") : void 0);
|
|
23088
|
+
let id = randomUUID();
|
|
23089
|
+
if (oldFile && existsSync2(oldFile))
|
|
23090
|
+
id = external_exports.uuid().parse(readFileSync(oldFile, "utf8").trim());
|
|
23091
|
+
mkdirSync(folder, { recursive: true, mode: 448 });
|
|
23092
|
+
const temporary = join5(folder, `.identity-${randomUUID()}`);
|
|
23093
|
+
try {
|
|
23094
|
+
writeFileSync(temporary, `${JSON.stringify({ id }, null, 2)}
|
|
23095
|
+
`, {
|
|
23096
|
+
mode: 384,
|
|
23097
|
+
flag: "wx"
|
|
23098
|
+
});
|
|
23099
|
+
try {
|
|
23100
|
+
linkSync(temporary, path2);
|
|
23101
|
+
} catch (error51) {
|
|
23102
|
+
if (error51.code !== "EEXIST") throw error51;
|
|
23103
|
+
}
|
|
23104
|
+
} finally {
|
|
23105
|
+
if (existsSync2(temporary)) unlinkSync(temporary);
|
|
23106
|
+
}
|
|
23107
|
+
return read2();
|
|
23108
|
+
};
|
|
23109
|
+
|
|
23110
|
+
// src/resolveSessionMachine.ts
|
|
23111
|
+
var resolveSessionMachine = (config2) => {
|
|
23112
|
+
const { id } = machineIdentity();
|
|
23113
|
+
if (process.env.BERTH_MACHINE_ID && process.env.BERTH_MACHINE_ID !== id)
|
|
23114
|
+
throw new Error(
|
|
23115
|
+
`BERTH_MACHINE_ID does not match this machine's identity (${id}).`
|
|
23116
|
+
);
|
|
23117
|
+
const machine = config2.workMachines.find((item) => item.id === id);
|
|
23118
|
+
if (!machine)
|
|
23119
|
+
throw new Error(
|
|
23120
|
+
`This machine (${id}) is not configured. Configure it in Berth desktop work machine settings.`
|
|
23121
|
+
);
|
|
23122
|
+
return machine;
|
|
23123
|
+
};
|
|
23124
|
+
|
|
23069
23125
|
// src/readTaskLaunchContext.ts
|
|
23070
23126
|
var readTaskLaunchContext = async (value) => {
|
|
23071
23127
|
const taskUuid = external_exports.uuid().parse(value);
|
|
@@ -23080,29 +23136,8 @@ var readTaskLaunchContext = async (value) => {
|
|
|
23080
23136
|
throw new Error("Task project is unavailable.");
|
|
23081
23137
|
const config2 = await client.config.read.query();
|
|
23082
23138
|
const osHostname = hostname3();
|
|
23083
|
-
const configuredId = process.env.BERTH_MACHINE_ID;
|
|
23084
23139
|
const currentCwd = await realpath4(process.cwd()).catch(() => null);
|
|
23085
|
-
const
|
|
23086
|
-
if (!configuredId && currentCwd) {
|
|
23087
|
-
for (const item of config2.workMachines) {
|
|
23088
|
-
const folder = item.instancesFolder === "~" ? homedir5() : item.instancesFolder.startsWith("~/") ? join5(homedir5(), item.instancesFolder.slice(2)) : item.instancesFolder;
|
|
23089
|
-
const root = await realpath4(folder).catch(() => null);
|
|
23090
|
-
if (!root || dirname3(currentCwd) !== root) continue;
|
|
23091
|
-
const registered = await listInstances(item.instancesFolder);
|
|
23092
|
-
if (registered.some(
|
|
23093
|
-
(instance) => instance.taskId === taskUuid && instance.slug === basename(currentCwd)
|
|
23094
|
-
))
|
|
23095
|
-
cwdMatches.push(item);
|
|
23096
|
-
}
|
|
23097
|
-
}
|
|
23098
|
-
const matches = config2.workMachines.filter(
|
|
23099
|
-
(item) => configuredId ? item.id === configuredId : item.name === osHostname
|
|
23100
|
-
);
|
|
23101
|
-
const machine = configuredId ? matches.length === 1 ? matches[0] : void 0 : cwdMatches.length === 1 ? cwdMatches[0] : cwdMatches.length > 1 ? void 0 : matches.length === 1 ? matches[0] : config2.workMachines.length === 1 ? config2.workMachines[0] : void 0;
|
|
23102
|
-
if (!machine)
|
|
23103
|
-
throw new Error(
|
|
23104
|
-
"Work machine is ambiguous. Set BERTH_MACHINE_ID to a configured machine UUID."
|
|
23105
|
-
);
|
|
23140
|
+
const machine = resolveSessionMachine(config2);
|
|
23106
23141
|
const all = await listInstances(machine.instancesFolder);
|
|
23107
23142
|
const linked = all.filter((instance) => instance.taskId === taskUuid);
|
|
23108
23143
|
if (linked.some((instance) => instance.projectId !== task.projectId))
|
|
@@ -23205,14 +23240,7 @@ var runInstanceCommand = async (args2) => {
|
|
|
23205
23240
|
`Task ${taskSlug} has no immutable ID; migrate it before linking an instance.`
|
|
23206
23241
|
);
|
|
23207
23242
|
const config2 = await client.config.read.query();
|
|
23208
|
-
const
|
|
23209
|
-
(machine2) => machine2.id === process.env.BERTH_MACHINE_ID || !process.env.BERTH_MACHINE_ID && machine2.name === hostname4()
|
|
23210
|
-
);
|
|
23211
|
-
const machine = matches.length === 1 ? matches[0] : !process.env.BERTH_MACHINE_ID && config2.workMachines.length === 1 ? config2.workMachines[0] : void 0;
|
|
23212
|
-
if (!machine)
|
|
23213
|
-
throw new Error(
|
|
23214
|
-
"Configure a work machine for this hostname, or set BERTH_MACHINE_ID to select one."
|
|
23215
|
-
);
|
|
23243
|
+
const machine = resolveSessionMachine(config2);
|
|
23216
23244
|
return prepareInstance({
|
|
23217
23245
|
projectId: project.id,
|
|
23218
23246
|
slug,
|
|
@@ -23223,6 +23251,36 @@ var runInstanceCommand = async (args2) => {
|
|
|
23223
23251
|
});
|
|
23224
23252
|
};
|
|
23225
23253
|
|
|
23254
|
+
// ../../packages/core/src/work/schemas/messageEventSchema.ts
|
|
23255
|
+
var messageEventSchema = external_exports.strictObject({
|
|
23256
|
+
at: external_exports.iso.datetime(),
|
|
23257
|
+
actor: external_exports.string().trim().min(1).nullable(),
|
|
23258
|
+
action: external_exports.enum([
|
|
23259
|
+
"assigned",
|
|
23260
|
+
"commented",
|
|
23261
|
+
"closed",
|
|
23262
|
+
"reopened",
|
|
23263
|
+
"requested",
|
|
23264
|
+
"approved",
|
|
23265
|
+
"rejected",
|
|
23266
|
+
"deferred",
|
|
23267
|
+
"restored"
|
|
23268
|
+
]),
|
|
23269
|
+
assignee: external_exports.string().trim().min(1).nullable().optional(),
|
|
23270
|
+
request: external_exports.strictObject({
|
|
23271
|
+
summary: external_exports.string(),
|
|
23272
|
+
evidence: external_exports.string(),
|
|
23273
|
+
deliveryAttemptId: external_exports.string().nullable()
|
|
23274
|
+
}).optional(),
|
|
23275
|
+
note: external_exports.string().trim().max(8e3).optional()
|
|
23276
|
+
});
|
|
23277
|
+
|
|
23278
|
+
// ../../packages/core/src/work/schemas/messageAppendSchema.ts
|
|
23279
|
+
var messageAppendSchema = messageEventSchema.omit({ at: true, request: true }).extend({
|
|
23280
|
+
id: external_exports.string().min(1),
|
|
23281
|
+
expectedLength: external_exports.number().int().nonnegative()
|
|
23282
|
+
});
|
|
23283
|
+
|
|
23226
23284
|
// ../../packages/core/src/work/schemas/messageTypeSchema.ts
|
|
23227
23285
|
var messageTypeSchema = external_exports.string().trim().min(1).max(100).regex(/^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/);
|
|
23228
23286
|
|
|
@@ -23247,6 +23305,7 @@ var messageListSchema = external_exports.strictObject({
|
|
|
23247
23305
|
// ../../packages/core/src/work/schemas/messageCreateSchema.ts
|
|
23248
23306
|
var messageCreateSchema = external_exports.strictObject({
|
|
23249
23307
|
recipient: messageRecipientSchema,
|
|
23308
|
+
sender: external_exports.string().trim().min(1).nullable().default(null),
|
|
23250
23309
|
type: messageTypeSchema.default("message"),
|
|
23251
23310
|
priority: external_exports.number().int().default(0),
|
|
23252
23311
|
projectId: slugSchema.nullable(),
|
|
@@ -23255,14 +23314,149 @@ var messageCreateSchema = external_exports.strictObject({
|
|
|
23255
23314
|
body: external_exports.string().trim().min(1).max(8e3)
|
|
23256
23315
|
});
|
|
23257
23316
|
|
|
23317
|
+
// ../../packages/core/src/work/schemas/workerSchema.ts
|
|
23318
|
+
var workerSchema = external_exports.strictObject({
|
|
23319
|
+
threadId: external_exports.string().trim().min(1).max(200).describe("Stable worker thread ID."),
|
|
23320
|
+
host: external_exports.string().trim().min(1).max(200).describe("Host identifier for the worker thread, such as local.")
|
|
23321
|
+
}).describe(
|
|
23322
|
+
"Structured worker destination; an identifier is not authentication or credential approval."
|
|
23323
|
+
);
|
|
23324
|
+
|
|
23325
|
+
// ../../packages/core/src/work/schemas/blockerHandoffReceiptSchema.ts
|
|
23326
|
+
var blockerHandoffReceiptSchema = external_exports.strictObject({
|
|
23327
|
+
updateCount: external_exports.number().int().positive().describe(
|
|
23328
|
+
"Blocker update-log length identifying the original unblocked cycle."
|
|
23329
|
+
),
|
|
23330
|
+
consumer: slugSchema.describe("Controller that reserved this cycle."),
|
|
23331
|
+
worker: workerSchema,
|
|
23332
|
+
reservedAt: external_exports.iso.datetime().describe("Time the reservation was saved."),
|
|
23333
|
+
delivery: external_exports.strictObject({
|
|
23334
|
+
receiptId: external_exports.string().trim().min(1).max(200).describe("External admitted turn or delivery receipt ID."),
|
|
23335
|
+
confirmedAt: external_exports.iso.datetime().describe("Time delivery was recorded."),
|
|
23336
|
+
sourceUpdateId: external_exports.uuid().optional().describe(
|
|
23337
|
+
"Existing informational update used to import a historical receipt."
|
|
23338
|
+
)
|
|
23339
|
+
}).nullable().describe(
|
|
23340
|
+
"Null means delivery is uncertain or pending; reconcile before sending. A populated delivery must not be resent."
|
|
23341
|
+
)
|
|
23342
|
+
}).describe(
|
|
23343
|
+
"Durable handoff receipt keyed by blocker update cycle; retained when reblocked."
|
|
23344
|
+
);
|
|
23345
|
+
|
|
23346
|
+
// ../../packages/core/src/work/schemas/blockerEventSchema.ts
|
|
23347
|
+
var blockerEventSchema = external_exports.strictObject({
|
|
23348
|
+
state: external_exports.enum(["blocked", "unblocked"]).describe(
|
|
23349
|
+
"blocked is recorded by controller; unblocked is recorded by user."
|
|
23350
|
+
),
|
|
23351
|
+
notes: external_exports.string().max(4e3).describe("Feedback or resolution notes for this change. Markdown.")
|
|
23352
|
+
}).describe(
|
|
23353
|
+
"An append-only update in the blocker\u2019s embedded log, oldest first."
|
|
23354
|
+
);
|
|
23355
|
+
|
|
23258
23356
|
// ../../packages/core/src/work/schemas/messageStatusSchema.ts
|
|
23259
23357
|
var messageStatusSchema = external_exports.enum(["active", "later", "unblocked", "archived"]).describe(
|
|
23260
23358
|
"Message lifecycle: active, later, unblocked or archived. Berth applies no type-specific transitions."
|
|
23261
23359
|
);
|
|
23262
23360
|
|
|
23361
|
+
// ../../packages/core/src/work/schemas/blockerStatusSchema.ts
|
|
23362
|
+
var blockerStatusSchema = messageStatusSchema.describe(
|
|
23363
|
+
"active: user queue; later: deferred, still blocking; unblocked: resolved by the user, awaiting controller acknowledgment (whether claimed or not); archived: acknowledged. Use queue commands to transition."
|
|
23364
|
+
);
|
|
23365
|
+
|
|
23366
|
+
// ../../packages/core/src/work/schemas/blockerBaseSchema.ts
|
|
23367
|
+
var blockerBaseSchema = external_exports.strictObject({
|
|
23368
|
+
id: external_exports.uuid().describe(
|
|
23369
|
+
"Immutable UUID generated by the server when this item is created."
|
|
23370
|
+
),
|
|
23371
|
+
taskSlug: taskSlugSchema.describe(
|
|
23372
|
+
"The existing task this blocker belongs to."
|
|
23373
|
+
),
|
|
23374
|
+
status: blockerStatusSchema,
|
|
23375
|
+
updates: external_exports.array(blockerEventSchema).min(1).describe(
|
|
23376
|
+
"Embedded state change log, oldest first. Appended only by state-transition commands."
|
|
23377
|
+
),
|
|
23378
|
+
handoffs: external_exports.array(blockerHandoffReceiptSchema).default([]).describe(
|
|
23379
|
+
"Durable diagnostics handoffs, one per unblocked update cycle."
|
|
23380
|
+
),
|
|
23381
|
+
createdAt: external_exports.iso.datetime().nullable().describe(
|
|
23382
|
+
"Creation time in UTC, or null for imported records whose time is unknown."
|
|
23383
|
+
),
|
|
23384
|
+
completedAt: external_exports.iso.datetime().nullable().describe(
|
|
23385
|
+
"Time the user marked unblocked; null before unblocking or for legacy completions."
|
|
23386
|
+
),
|
|
23387
|
+
acknowledgedAt: external_exports.iso.datetime().nullable().describe(
|
|
23388
|
+
"Time the consumer acknowledged the item, or null before acknowledgment."
|
|
23389
|
+
),
|
|
23390
|
+
dismissedAt: external_exports.iso.datetime().nullable().optional().describe(
|
|
23391
|
+
"Time the user hid this pending-review cycle from the web inbox; controller acknowledgment is independent."
|
|
23392
|
+
),
|
|
23393
|
+
consumer: slugSchema.nullable().describe(
|
|
23394
|
+
"Controller consumer owning this acknowledgment queue item. Set atomically by pull; null before pulling."
|
|
23395
|
+
),
|
|
23396
|
+
outcome: external_exports.string().max(4e3).describe(
|
|
23397
|
+
"User completion note or legacy resolution; may be empty. Markdown."
|
|
23398
|
+
),
|
|
23399
|
+
selectedOptionIndex: external_exports.number().int().min(-1).nullable().describe(
|
|
23400
|
+
"Zero-based chosen decision option, or -1 for None of the above. Null for actions or decisions without a recorded choice."
|
|
23401
|
+
)
|
|
23402
|
+
}).describe(
|
|
23403
|
+
"Server-managed identity, queue state and completion receipt for a blocker."
|
|
23404
|
+
);
|
|
23405
|
+
|
|
23406
|
+
// ../../packages/core/src/work/schemas/prioritySchema.ts
|
|
23407
|
+
var prioritySchema = external_exports.enum(["high", "medium", "low", "unset"]).describe("Priority: high, medium, low, or unset when not assigned.");
|
|
23408
|
+
|
|
23409
|
+
// ../../packages/core/src/work/schemas/decisionOptionSchema.ts
|
|
23410
|
+
var decisionOptionSchema = external_exports.strictObject({
|
|
23411
|
+
title: external_exports.string().trim().min(1).max(120).describe("Short name for this choice."),
|
|
23412
|
+
body: external_exports.string().trim().min(1).max(4e3).describe(
|
|
23413
|
+
"Explain this option, its consequences and tradeoffs. Markdown."
|
|
23414
|
+
),
|
|
23415
|
+
priority: prioritySchema.describe(
|
|
23416
|
+
"Preference for this option: high, medium, low or unset. High is the strongest recommendation."
|
|
23417
|
+
)
|
|
23418
|
+
}).describe("One selectable option in a decision.");
|
|
23419
|
+
|
|
23420
|
+
// ../../packages/core/src/work/schemas/decisionSchema.ts
|
|
23421
|
+
var decisionSchema = external_exports.strictObject({
|
|
23422
|
+
background: external_exports.string().trim().min(1).max(4e3).describe("Context the user needs to understand the decision. Markdown."),
|
|
23423
|
+
problem: external_exports.string().trim().min(1).max(4e3).describe("The problem or question that needs a decision. Markdown."),
|
|
23424
|
+
options: external_exports.array(decisionOptionSchema).max(12).describe(
|
|
23425
|
+
"Predefined choices presented to the user. The UI always adds None of the above; do not include it in this array. New decisions require at least one option; legacy decisions may have none. Completion records the chosen index or -1 for None of the above."
|
|
23426
|
+
)
|
|
23427
|
+
}).describe("Decision content: background, problem, and prioritized options.");
|
|
23428
|
+
|
|
23429
|
+
// ../../packages/core/src/work/schemas/actionSchema.ts
|
|
23430
|
+
var actionSchema = external_exports.strictObject({
|
|
23431
|
+
background: external_exports.string().trim().min(1).max(4e3).describe("Context explaining why this action is needed. Markdown."),
|
|
23432
|
+
instructions: external_exports.string().trim().min(1).max(6e3).describe(
|
|
23433
|
+
"Concrete instructions for the user to carry out before marking done. Markdown."
|
|
23434
|
+
)
|
|
23435
|
+
}).describe("Action content: background and instructions.");
|
|
23436
|
+
|
|
23437
|
+
// ../../packages/core/src/work/schemas/blockerSchema.ts
|
|
23438
|
+
var blockerSchema = external_exports.discriminatedUnion("type", [
|
|
23439
|
+
blockerBaseSchema.extend({
|
|
23440
|
+
...decisionSchema.shape,
|
|
23441
|
+
type: external_exports.literal("decision").describe(
|
|
23442
|
+
"A one-time decision. Once submitted its choice is final; create a new decision for a new question."
|
|
23443
|
+
)
|
|
23444
|
+
}),
|
|
23445
|
+
blockerBaseSchema.extend({
|
|
23446
|
+
...actionSchema.shape,
|
|
23447
|
+
type: external_exports.literal("action").describe(
|
|
23448
|
+
"An action requiring instructions to be carried out. It may be returned with feedback and actioned again."
|
|
23449
|
+
)
|
|
23450
|
+
})
|
|
23451
|
+
]).describe(
|
|
23452
|
+
"A typed blocker and its queue lifecycle. Completed content is immutable until archived so consumers receive stable results."
|
|
23453
|
+
);
|
|
23454
|
+
|
|
23263
23455
|
// ../../packages/core/src/work/schemas/messageSchema.ts
|
|
23264
23456
|
var messageSchema = external_exports.strictObject({
|
|
23265
23457
|
id: external_exports.string(),
|
|
23458
|
+
sender: external_exports.string().min(1).nullable().optional(),
|
|
23459
|
+
timeline: external_exports.array(messageEventSchema).optional(),
|
|
23266
23460
|
type: messageTypeSchema,
|
|
23267
23461
|
recipient: messageRecipientSchema,
|
|
23268
23462
|
priority: external_exports.number().int(),
|
|
@@ -23274,7 +23468,8 @@ var messageSchema = external_exports.strictObject({
|
|
|
23274
23468
|
status: messageStatusSchema,
|
|
23275
23469
|
claimedAt: external_exports.iso.datetime().nullable(),
|
|
23276
23470
|
consumer: external_exports.string().nullable(),
|
|
23277
|
-
sourceId: external_exports.string()
|
|
23471
|
+
sourceId: external_exports.string(),
|
|
23472
|
+
blocker: blockerSchema.optional()
|
|
23278
23473
|
});
|
|
23279
23474
|
|
|
23280
23475
|
// ../../packages/core/src/work/schemas/messageUpdateSchema.ts
|
|
@@ -23295,28 +23490,33 @@ var runMessageCommand = async (args2) => {
|
|
|
23295
23490
|
if (extra.length) throw new Error("Quote the JSON payload as one argument.");
|
|
23296
23491
|
if (action === "schema") {
|
|
23297
23492
|
const schemas = {
|
|
23493
|
+
append: messageAppendSchema,
|
|
23298
23494
|
create: messageCreateSchema,
|
|
23299
23495
|
update: messageUpdateSchema,
|
|
23300
23496
|
list: messageListSchema
|
|
23301
23497
|
};
|
|
23302
23498
|
const key = input ?? "create";
|
|
23303
|
-
if (key !== "create" && key !== "update" && key !== "list")
|
|
23304
|
-
throw new Error("Choose create, update or list schema.");
|
|
23499
|
+
if (key !== "append" && key !== "create" && key !== "update" && key !== "list")
|
|
23500
|
+
throw new Error("Choose create, update, append or list schema.");
|
|
23305
23501
|
return external_exports.toJSONSchema(schemas[key], { io: "input" });
|
|
23306
23502
|
}
|
|
23307
23503
|
const api = createWorkClient().message;
|
|
23308
23504
|
if (action === "types" && input === void 0) return api.types.query();
|
|
23505
|
+
if (action === "read" && typeof input === "string")
|
|
23506
|
+
return api.read.query(input);
|
|
23309
23507
|
const payload = input === void 0 ? void 0 : JSON.parse(input);
|
|
23310
23508
|
if (action === "list")
|
|
23311
23509
|
return api.list.query(messageListSchema.optional().parse(payload));
|
|
23312
23510
|
if (action === "read" && typeof input === "string")
|
|
23313
23511
|
return api.read.query(input);
|
|
23512
|
+
if (action === "append")
|
|
23513
|
+
return api.append.mutate(messageAppendSchema.parse(payload));
|
|
23314
23514
|
if (action === "create")
|
|
23315
23515
|
return api.create.mutate(messageCreateSchema.parse(payload));
|
|
23316
23516
|
if (action === "update")
|
|
23317
23517
|
return api.update.mutate(messageUpdateSchema.parse(payload));
|
|
23318
23518
|
throw new Error(
|
|
23319
|
-
"Messages support types, create, update, read, list and schema."
|
|
23519
|
+
"Messages support types, create, update, append, read, list and schema."
|
|
23320
23520
|
);
|
|
23321
23521
|
};
|
|
23322
23522
|
|
|
@@ -23463,9 +23663,6 @@ var projectReleaseTargetSchema = external_exports.strictObject({
|
|
|
23463
23663
|
"One independently released part of a project: its icon, name, and URL."
|
|
23464
23664
|
);
|
|
23465
23665
|
|
|
23466
|
-
// ../../packages/core/src/work/schemas/prioritySchema.ts
|
|
23467
|
-
var prioritySchema = external_exports.enum(["high", "medium", "low", "unset"]).describe("Priority: high, medium, low, or unset when not assigned.");
|
|
23468
|
-
|
|
23469
23666
|
// ../../packages/core/src/work/schemas/projectRepoSchema.ts
|
|
23470
23667
|
var projectRepoSchema = external_exports.strictObject({
|
|
23471
23668
|
url: linkSchema.refine((url2) => url2.length > 0, "Enter a repo URL."),
|
|
@@ -23691,14 +23888,6 @@ var taskSignOffSchema = external_exports.strictObject({
|
|
|
23691
23888
|
"User sign-off for a task. Set only through the sign-off endpoint, never through task CRUD."
|
|
23692
23889
|
);
|
|
23693
23890
|
|
|
23694
|
-
// ../../packages/core/src/work/schemas/workerSchema.ts
|
|
23695
|
-
var workerSchema = external_exports.strictObject({
|
|
23696
|
-
threadId: external_exports.string().trim().min(1).max(200).describe("Stable worker thread ID."),
|
|
23697
|
-
host: external_exports.string().trim().min(1).max(200).describe("Host identifier for the worker thread, such as local.")
|
|
23698
|
-
}).describe(
|
|
23699
|
-
"Structured worker destination; an identifier is not authentication or credential approval."
|
|
23700
|
-
);
|
|
23701
|
-
|
|
23702
23891
|
// ../../packages/core/src/work/schemas/terminalWorkerSchema.ts
|
|
23703
23892
|
var terminalWorkerSchema = external_exports.strictObject({
|
|
23704
23893
|
sessionId: external_exports.uuid().describe("Berth terminal session UUID."),
|
|
@@ -23913,6 +24102,22 @@ var runPromptCommand = async (args2) => {
|
|
|
23913
24102
|
);
|
|
23914
24103
|
};
|
|
23915
24104
|
|
|
24105
|
+
// ../../packages/core/src/work/data/getSessionRecoveryProblem.ts
|
|
24106
|
+
var getSessionRecoveryProblem = (session, task) => {
|
|
24107
|
+
if (!task || session.taskId !== task.uuid || task.status !== "working" || task.archivedAt)
|
|
24108
|
+
return "Task is not in the working column.";
|
|
24109
|
+
if (task.workerRun?.completedAt)
|
|
24110
|
+
return "This worker has already completed its work.";
|
|
24111
|
+
const owner = task.ownership?.worker;
|
|
24112
|
+
if (owner && (!("sessionId" in owner) || owner.sessionId !== session.id || owner.machineId !== session.machineId))
|
|
24113
|
+
return "Task is assigned to another worker.";
|
|
24114
|
+
if (task.workerId && task.workerId !== session.workerSessionId)
|
|
24115
|
+
return "Task is assigned to another worker.";
|
|
24116
|
+
if (session.endedAt || !session.startedAt)
|
|
24117
|
+
return "Session is not an unexpectedly exited session.";
|
|
24118
|
+
return null;
|
|
24119
|
+
};
|
|
24120
|
+
|
|
23916
24121
|
// ../../packages/core/src/work/data/renderAgentPrompt.ts
|
|
23917
24122
|
var import_handlebars = __toESM(require_lib(), 1);
|
|
23918
24123
|
|
|
@@ -23932,7 +24137,7 @@ var renderAgentPrompt = (prompts, key, variables) => import_handlebars.default.c
|
|
|
23932
24137
|
})(variables);
|
|
23933
24138
|
|
|
23934
24139
|
// src/runSessionCommand.ts
|
|
23935
|
-
import { setTimeout as
|
|
24140
|
+
import { setTimeout as delay3 } from "node:timers/promises";
|
|
23936
24141
|
import { realpathSync } from "node:fs";
|
|
23937
24142
|
import { homedir as homedir6 } from "node:os";
|
|
23938
24143
|
import { join as join6 } from "node:path";
|
|
@@ -23990,56 +24195,8 @@ var findWorkerType = (types, id) => {
|
|
|
23990
24195
|
return type;
|
|
23991
24196
|
};
|
|
23992
24197
|
|
|
23993
|
-
//
|
|
23994
|
-
|
|
23995
|
-
|
|
23996
|
-
// src/prepareTaskWorkerInstance.ts
|
|
23997
|
-
var prepareTaskWorkerInstance = async (taskId, instanceSlug) => {
|
|
23998
|
-
external_exports.uuid().parse(taskId);
|
|
23999
|
-
const config2 = await createWorkClient().config.read.query();
|
|
24000
|
-
const workerTypeId = resolveDefaultWorkerTypeId(
|
|
24001
|
-
config2.workerTypes ?? [],
|
|
24002
|
-
config2.defaultWorkerTypeId
|
|
24003
|
-
);
|
|
24004
|
-
if (!workerTypeId) throw new Error("Configure a default worker type first.");
|
|
24005
|
-
findWorkerType(config2.workerTypes ?? [], workerTypeId);
|
|
24006
|
-
const context = await readTaskLaunchContext(taskId);
|
|
24007
|
-
if (!["draft", "queue"].includes(context.task.status))
|
|
24008
|
-
throw new Error(
|
|
24009
|
-
"Only an unstarted Draft or Queue task can start a worker."
|
|
24010
|
-
);
|
|
24011
|
-
if (context.task.blocked || context.task.ownership?.worker || context.task.workerId)
|
|
24012
|
-
throw new Error("Task is blocked or already assigned to a worker.");
|
|
24013
|
-
if (context.state === "missing_instance") {
|
|
24014
|
-
if (!context.project) throw new Error("Task has no project to prepare.");
|
|
24015
|
-
await runInstanceCommand([
|
|
24016
|
-
"prepare",
|
|
24017
|
-
"--project",
|
|
24018
|
-
context.project.id,
|
|
24019
|
-
"--task",
|
|
24020
|
-
context.task.slug,
|
|
24021
|
-
...instanceSlug ? ["--slug", instanceSlug] : []
|
|
24022
|
-
]);
|
|
24023
|
-
} else if (context.state !== "ready") {
|
|
24024
|
-
throw new Error(`Task instance is ${context.state}; cannot start worker.`);
|
|
24025
|
-
}
|
|
24026
|
-
return workerTypeId;
|
|
24027
|
-
};
|
|
24028
|
-
|
|
24029
|
-
// src/resolveSessionMachine.ts
|
|
24030
|
-
import { hostname as hostname5 } from "node:os";
|
|
24031
|
-
var resolveSessionMachine = (config2) => {
|
|
24032
|
-
const configured = process.env.BERTH_MACHINE_ID;
|
|
24033
|
-
const matches = config2.workMachines.filter(
|
|
24034
|
-
(item) => configured ? item.id === configured : item.name === hostname5()
|
|
24035
|
-
);
|
|
24036
|
-
const machine = matches.length === 1 ? matches[0] : !configured && config2.workMachines.length === 1 ? config2.workMachines[0] : void 0;
|
|
24037
|
-
if (!machine)
|
|
24038
|
-
throw new Error(
|
|
24039
|
-
"Work machine is ambiguous. Set BERTH_MACHINE_ID to a configured machine UUID."
|
|
24040
|
-
);
|
|
24041
|
-
return machine;
|
|
24042
|
-
};
|
|
24198
|
+
// src/recoverTerminalSession.ts
|
|
24199
|
+
import { setTimeout as delay2 } from "node:timers/promises";
|
|
24043
24200
|
|
|
24044
24201
|
// src/tmuxSession.ts
|
|
24045
24202
|
import { spawnSync } from "node:child_process";
|
|
@@ -24122,6 +24279,79 @@ var attachTmuxSession = (name) => {
|
|
|
24122
24279
|
throw new Error("Could not attach to the tmux session.");
|
|
24123
24280
|
};
|
|
24124
24281
|
|
|
24282
|
+
// src/recoverTerminalSession.ts
|
|
24283
|
+
var recoverTerminalSession = async (session, worker) => {
|
|
24284
|
+
if (session.endedAt || !session.startedAt)
|
|
24285
|
+
throw new Error("Session is not an unexpectedly exited session.");
|
|
24286
|
+
if (tmuxSessionExists(session.terminalSessionId))
|
|
24287
|
+
throw new Error("Session is already running.");
|
|
24288
|
+
if (session.workerTypeId !== "shell" && (!worker?.resumeCommand || !session.workerSessionId))
|
|
24289
|
+
throw new Error(
|
|
24290
|
+
"Worker has no configured resume command or conversation ID."
|
|
24291
|
+
);
|
|
24292
|
+
if (worker && !/{{\s*prompt\s*}}/.test(worker.resumeCommand))
|
|
24293
|
+
throw new Error("Resume command must include {{prompt}} to continue work.");
|
|
24294
|
+
if (worker && !/{{\s*workerSessionId\s*}}/.test(worker.resumeCommand))
|
|
24295
|
+
throw new Error(
|
|
24296
|
+
"Resume command must include {{workerSessionId}} to resume the original conversation."
|
|
24297
|
+
);
|
|
24298
|
+
const command = worker ? renderWorkerCommand(worker.resumeCommand, {
|
|
24299
|
+
prompt: `Your terminal was interrupted. Review task ${session.taskId ?? ""} and your prior progress, then continue working. Respect any existing blockers and do not repeat completed actions.`,
|
|
24300
|
+
sessionId: session.id,
|
|
24301
|
+
workerSessionId: session.workerSessionId ?? "",
|
|
24302
|
+
taskId: session.taskId ?? "",
|
|
24303
|
+
directory: session.directory,
|
|
24304
|
+
terminalSessionId: session.terminalSessionId
|
|
24305
|
+
}) : null;
|
|
24306
|
+
startTmuxSession(
|
|
24307
|
+
session.terminalSessionId,
|
|
24308
|
+
session.directory,
|
|
24309
|
+
command,
|
|
24310
|
+
session.id,
|
|
24311
|
+
session.machineId
|
|
24312
|
+
);
|
|
24313
|
+
await delay2(250);
|
|
24314
|
+
if (!tmuxSessionExists(session.terminalSessionId))
|
|
24315
|
+
throw new Error("Worker exited immediately after recovery.");
|
|
24316
|
+
return { id: session.id, recovered: true };
|
|
24317
|
+
};
|
|
24318
|
+
|
|
24319
|
+
// ../../packages/core/src/work/data/resolveDefaultWorkerTypeId.ts
|
|
24320
|
+
var resolveDefaultWorkerTypeId = (types, current) => types.some((type) => type.id === current) ? current : types[0]?.id ?? null;
|
|
24321
|
+
|
|
24322
|
+
// src/prepareTaskWorkerInstance.ts
|
|
24323
|
+
var prepareTaskWorkerInstance = async (taskId, instanceSlug) => {
|
|
24324
|
+
external_exports.uuid().parse(taskId);
|
|
24325
|
+
const config2 = await createWorkClient().config.read.query();
|
|
24326
|
+
const workerTypeId = resolveDefaultWorkerTypeId(
|
|
24327
|
+
config2.workerTypes ?? [],
|
|
24328
|
+
config2.defaultWorkerTypeId
|
|
24329
|
+
);
|
|
24330
|
+
if (!workerTypeId) throw new Error("Configure a default worker type first.");
|
|
24331
|
+
findWorkerType(config2.workerTypes ?? [], workerTypeId);
|
|
24332
|
+
const context = await readTaskLaunchContext(taskId);
|
|
24333
|
+
if (!["draft", "queue"].includes(context.task.status))
|
|
24334
|
+
throw new Error(
|
|
24335
|
+
"Only an unstarted Draft or Queue task can start a worker."
|
|
24336
|
+
);
|
|
24337
|
+
if (context.task.blocked || context.task.ownership?.worker || context.task.workerId)
|
|
24338
|
+
throw new Error("Task is blocked or already assigned to a worker.");
|
|
24339
|
+
if (context.state === "missing_instance") {
|
|
24340
|
+
if (!context.project) throw new Error("Task has no project to prepare.");
|
|
24341
|
+
await runInstanceCommand([
|
|
24342
|
+
"prepare",
|
|
24343
|
+
"--project",
|
|
24344
|
+
context.project.id,
|
|
24345
|
+
"--task",
|
|
24346
|
+
context.task.slug,
|
|
24347
|
+
...instanceSlug ? ["--slug", instanceSlug] : []
|
|
24348
|
+
]);
|
|
24349
|
+
} else if (context.state !== "ready") {
|
|
24350
|
+
throw new Error(`Task instance is ${context.state}; cannot start worker.`);
|
|
24351
|
+
}
|
|
24352
|
+
return workerTypeId;
|
|
24353
|
+
};
|
|
24354
|
+
|
|
24125
24355
|
// src/runSessionCommand.ts
|
|
24126
24356
|
var runSessionCommand = async (args2) => {
|
|
24127
24357
|
const [action, value, agentValue, instanceSlug, ...extra] = args2;
|
|
@@ -24147,6 +24377,27 @@ var runSessionCommand = async (args2) => {
|
|
|
24147
24377
|
live: session.machineId === machine.id ? tmuxSessionExists(session.terminalSessionId) : null
|
|
24148
24378
|
};
|
|
24149
24379
|
}
|
|
24380
|
+
if (action === "recover" && value && !agentValue) {
|
|
24381
|
+
const session = await client.terminalSession.read.query(
|
|
24382
|
+
external_exports.uuid().parse(value)
|
|
24383
|
+
);
|
|
24384
|
+
const config2 = await client.config.read.query();
|
|
24385
|
+
const machine = resolveSessionMachine(config2);
|
|
24386
|
+
if (session.machineId !== machine.id)
|
|
24387
|
+
throw new Error("Session belongs to another work machine.");
|
|
24388
|
+
if (!session.taskId)
|
|
24389
|
+
throw new Error("Only task sessions can be recovered.");
|
|
24390
|
+
const tasks = await client.task.list.query({ status: "working" });
|
|
24391
|
+
const problem = getSessionRecoveryProblem(
|
|
24392
|
+
session,
|
|
24393
|
+
tasks.find((task) => task.uuid === session.taskId)
|
|
24394
|
+
);
|
|
24395
|
+
if (problem) throw new Error(problem);
|
|
24396
|
+
return recoverTerminalSession(
|
|
24397
|
+
session,
|
|
24398
|
+
session.workerTypeId === "shell" ? null : findWorkerType(config2.workerTypes ?? [], session.workerTypeId)
|
|
24399
|
+
);
|
|
24400
|
+
}
|
|
24150
24401
|
if (action === "attach" && value && !agentValue) {
|
|
24151
24402
|
const session = await client.terminalSession.read.query(
|
|
24152
24403
|
external_exports.uuid().parse(value)
|
|
@@ -24218,7 +24469,7 @@ var runSessionCommand = async (args2) => {
|
|
|
24218
24469
|
session.id,
|
|
24219
24470
|
machine.id
|
|
24220
24471
|
);
|
|
24221
|
-
await
|
|
24472
|
+
await delay3(250);
|
|
24222
24473
|
if (!tmuxSessionExists(session.terminalSessionId))
|
|
24223
24474
|
throw new Error("Agent process exited immediately after launch.");
|
|
24224
24475
|
return client.terminalSession.update.mutate({
|
|
@@ -24324,7 +24575,7 @@ var runSessionCommand = async (args2) => {
|
|
|
24324
24575
|
session.id,
|
|
24325
24576
|
context.machine.id
|
|
24326
24577
|
);
|
|
24327
|
-
await
|
|
24578
|
+
await delay3(250);
|
|
24328
24579
|
if (!tmuxSessionExists(session.terminalSessionId))
|
|
24329
24580
|
throw new Error("Agent process exited immediately after launch.");
|
|
24330
24581
|
} catch (error51) {
|
|
@@ -24343,7 +24594,7 @@ var runSessionCommand = async (args2) => {
|
|
|
24343
24594
|
});
|
|
24344
24595
|
}
|
|
24345
24596
|
throw new Error(
|
|
24346
|
-
"Use berth session list|read|status|create|start|launch|attach|kill|delete|register|schema. Run berth --help."
|
|
24597
|
+
"Use berth session list|read|status|create|start|launch|recover|attach|kill|delete|register|schema. Run berth --help."
|
|
24347
24598
|
);
|
|
24348
24599
|
};
|
|
24349
24600
|
|
|
@@ -24437,6 +24688,9 @@ var runRoleCommand = async (args2) => {
|
|
|
24437
24688
|
// ../../packages/core/src/work/schemas/signOffRequestSchema.ts
|
|
24438
24689
|
var signOffRequestSchema = external_exports.strictObject({
|
|
24439
24690
|
slug: taskSlugSchema,
|
|
24691
|
+
sender: external_exports.string().trim().min(1).optional().describe(
|
|
24692
|
+
"Requesting role within the project; defaults to delivery-owner."
|
|
24693
|
+
),
|
|
24440
24694
|
deliveryAttemptId: external_exports.uuid().optional().describe(
|
|
24441
24695
|
"Latest recorded delivery attempt; omitted selects latest. Legacy issued requests remain valid without an attempt."
|
|
24442
24696
|
),
|
|
@@ -24448,128 +24702,6 @@ var signOffRequestSchema = external_exports.strictObject({
|
|
|
24448
24702
|
)
|
|
24449
24703
|
});
|
|
24450
24704
|
|
|
24451
|
-
// ../../packages/core/src/work/schemas/blockerHandoffReceiptSchema.ts
|
|
24452
|
-
var blockerHandoffReceiptSchema = external_exports.strictObject({
|
|
24453
|
-
updateCount: external_exports.number().int().positive().describe(
|
|
24454
|
-
"Blocker update-log length identifying the original unblocked cycle."
|
|
24455
|
-
),
|
|
24456
|
-
consumer: slugSchema.describe("Controller that reserved this cycle."),
|
|
24457
|
-
worker: workerSchema,
|
|
24458
|
-
reservedAt: external_exports.iso.datetime().describe("Time the reservation was saved."),
|
|
24459
|
-
delivery: external_exports.strictObject({
|
|
24460
|
-
receiptId: external_exports.string().trim().min(1).max(200).describe("External admitted turn or delivery receipt ID."),
|
|
24461
|
-
confirmedAt: external_exports.iso.datetime().describe("Time delivery was recorded."),
|
|
24462
|
-
sourceUpdateId: external_exports.uuid().optional().describe(
|
|
24463
|
-
"Existing informational update used to import a historical receipt."
|
|
24464
|
-
)
|
|
24465
|
-
}).nullable().describe(
|
|
24466
|
-
"Null means delivery is uncertain or pending; reconcile before sending. A populated delivery must not be resent."
|
|
24467
|
-
)
|
|
24468
|
-
}).describe(
|
|
24469
|
-
"Durable handoff receipt keyed by blocker update cycle; retained when reblocked."
|
|
24470
|
-
);
|
|
24471
|
-
|
|
24472
|
-
// ../../packages/core/src/work/schemas/blockerEventSchema.ts
|
|
24473
|
-
var blockerEventSchema = external_exports.strictObject({
|
|
24474
|
-
state: external_exports.enum(["blocked", "unblocked"]).describe(
|
|
24475
|
-
"blocked is recorded by controller; unblocked is recorded by user."
|
|
24476
|
-
),
|
|
24477
|
-
notes: external_exports.string().max(4e3).describe("Feedback or resolution notes for this change. Markdown.")
|
|
24478
|
-
}).describe(
|
|
24479
|
-
"An append-only update in the blocker\u2019s embedded log, oldest first."
|
|
24480
|
-
);
|
|
24481
|
-
|
|
24482
|
-
// ../../packages/core/src/work/schemas/blockerStatusSchema.ts
|
|
24483
|
-
var blockerStatusSchema = messageStatusSchema.describe(
|
|
24484
|
-
"active: user queue; later: deferred, still blocking; unblocked: resolved by the user, awaiting controller acknowledgment (whether claimed or not); archived: acknowledged. Use queue commands to transition."
|
|
24485
|
-
);
|
|
24486
|
-
|
|
24487
|
-
// ../../packages/core/src/work/schemas/blockerBaseSchema.ts
|
|
24488
|
-
var blockerBaseSchema = external_exports.strictObject({
|
|
24489
|
-
id: external_exports.uuid().describe(
|
|
24490
|
-
"Immutable UUID generated by the server when this item is created."
|
|
24491
|
-
),
|
|
24492
|
-
taskSlug: taskSlugSchema.describe(
|
|
24493
|
-
"The existing task this blocker belongs to."
|
|
24494
|
-
),
|
|
24495
|
-
status: blockerStatusSchema,
|
|
24496
|
-
updates: external_exports.array(blockerEventSchema).min(1).describe(
|
|
24497
|
-
"Embedded state change log, oldest first. Appended only by state-transition commands."
|
|
24498
|
-
),
|
|
24499
|
-
handoffs: external_exports.array(blockerHandoffReceiptSchema).default([]).describe(
|
|
24500
|
-
"Durable diagnostics handoffs, one per unblocked update cycle."
|
|
24501
|
-
),
|
|
24502
|
-
createdAt: external_exports.iso.datetime().nullable().describe(
|
|
24503
|
-
"Creation time in UTC, or null for imported records whose time is unknown."
|
|
24504
|
-
),
|
|
24505
|
-
completedAt: external_exports.iso.datetime().nullable().describe(
|
|
24506
|
-
"Time the user marked unblocked; null before unblocking or for legacy completions."
|
|
24507
|
-
),
|
|
24508
|
-
acknowledgedAt: external_exports.iso.datetime().nullable().describe(
|
|
24509
|
-
"Time the consumer acknowledged the item, or null before acknowledgment."
|
|
24510
|
-
),
|
|
24511
|
-
dismissedAt: external_exports.iso.datetime().nullable().optional().describe(
|
|
24512
|
-
"Time the user hid this pending-review cycle from the web inbox; controller acknowledgment is independent."
|
|
24513
|
-
),
|
|
24514
|
-
consumer: slugSchema.nullable().describe(
|
|
24515
|
-
"Controller consumer owning this acknowledgment queue item. Set atomically by pull; null before pulling."
|
|
24516
|
-
),
|
|
24517
|
-
outcome: external_exports.string().max(4e3).describe(
|
|
24518
|
-
"User completion note or legacy resolution; may be empty. Markdown."
|
|
24519
|
-
),
|
|
24520
|
-
selectedOptionIndex: external_exports.number().int().min(-1).nullable().describe(
|
|
24521
|
-
"Zero-based chosen decision option, or -1 for None of the above. Null for actions or decisions without a recorded choice."
|
|
24522
|
-
)
|
|
24523
|
-
}).describe(
|
|
24524
|
-
"Server-managed identity, queue state and completion receipt for a blocker."
|
|
24525
|
-
);
|
|
24526
|
-
|
|
24527
|
-
// ../../packages/core/src/work/schemas/decisionOptionSchema.ts
|
|
24528
|
-
var decisionOptionSchema = external_exports.strictObject({
|
|
24529
|
-
title: external_exports.string().trim().min(1).max(120).describe("Short name for this choice."),
|
|
24530
|
-
body: external_exports.string().trim().min(1).max(4e3).describe(
|
|
24531
|
-
"Explain this option, its consequences and tradeoffs. Markdown."
|
|
24532
|
-
),
|
|
24533
|
-
priority: prioritySchema.describe(
|
|
24534
|
-
"Preference for this option: high, medium, low or unset. High is the strongest recommendation."
|
|
24535
|
-
)
|
|
24536
|
-
}).describe("One selectable option in a decision.");
|
|
24537
|
-
|
|
24538
|
-
// ../../packages/core/src/work/schemas/decisionSchema.ts
|
|
24539
|
-
var decisionSchema = external_exports.strictObject({
|
|
24540
|
-
background: external_exports.string().trim().min(1).max(4e3).describe("Context the user needs to understand the decision. Markdown."),
|
|
24541
|
-
problem: external_exports.string().trim().min(1).max(4e3).describe("The problem or question that needs a decision. Markdown."),
|
|
24542
|
-
options: external_exports.array(decisionOptionSchema).max(12).describe(
|
|
24543
|
-
"Predefined choices presented to the user. The UI always adds None of the above; do not include it in this array. New decisions require at least one option; legacy decisions may have none. Completion records the chosen index or -1 for None of the above."
|
|
24544
|
-
)
|
|
24545
|
-
}).describe("Decision content: background, problem, and prioritized options.");
|
|
24546
|
-
|
|
24547
|
-
// ../../packages/core/src/work/schemas/actionSchema.ts
|
|
24548
|
-
var actionSchema = external_exports.strictObject({
|
|
24549
|
-
background: external_exports.string().trim().min(1).max(4e3).describe("Context explaining why this action is needed. Markdown."),
|
|
24550
|
-
instructions: external_exports.string().trim().min(1).max(6e3).describe(
|
|
24551
|
-
"Concrete instructions for the user to carry out before marking done. Markdown."
|
|
24552
|
-
)
|
|
24553
|
-
}).describe("Action content: background and instructions.");
|
|
24554
|
-
|
|
24555
|
-
// ../../packages/core/src/work/schemas/blockerSchema.ts
|
|
24556
|
-
var blockerSchema = external_exports.discriminatedUnion("type", [
|
|
24557
|
-
blockerBaseSchema.extend({
|
|
24558
|
-
...decisionSchema.shape,
|
|
24559
|
-
type: external_exports.literal("decision").describe(
|
|
24560
|
-
"A one-time decision. Once submitted its choice is final; create a new decision for a new question."
|
|
24561
|
-
)
|
|
24562
|
-
}),
|
|
24563
|
-
blockerBaseSchema.extend({
|
|
24564
|
-
...actionSchema.shape,
|
|
24565
|
-
type: external_exports.literal("action").describe(
|
|
24566
|
-
"An action requiring instructions to be carried out. It may be returned with feedback and actioned again."
|
|
24567
|
-
)
|
|
24568
|
-
})
|
|
24569
|
-
]).describe(
|
|
24570
|
-
"A typed blocker and its queue lifecycle. Completed content is immutable until archived so consumers receive stable results."
|
|
24571
|
-
);
|
|
24572
|
-
|
|
24573
24705
|
// ../../packages/core/src/work/schemas/blockerPollSchema.ts
|
|
24574
24706
|
var blockerPollSchema = external_exports.strictObject({
|
|
24575
24707
|
id: external_exports.uuid(),
|
|
@@ -25209,6 +25341,7 @@ berth session status <session-uuid>
|
|
|
25209
25341
|
berth session create [shell|worker-type-id] [directory]
|
|
25210
25342
|
berth session start <task-uuid> [instance-slug]
|
|
25211
25343
|
berth session launch <task-uuid> <worker-type-id> [instance-slug]
|
|
25344
|
+
berth session recover <session-uuid>
|
|
25212
25345
|
berth session attach <session-uuid>
|
|
25213
25346
|
berth session kill <session-uuid>
|
|
25214
25347
|
berth session delete <session-uuid>
|
|
@@ -25235,7 +25368,8 @@ berth message create '{"recipient":{"name":"delivery-owner","projectId":"bond"},
|
|
|
25235
25368
|
berth message update '{"id":"<message-id>","recipient":{"name":"delivery-owner","projectId":"bond"}}'
|
|
25236
25369
|
berth message read <message-id>
|
|
25237
25370
|
berth message list '{"recipient":{"name":"delivery-owner","projectId":"bond"}}'
|
|
25238
|
-
berth message
|
|
25371
|
+
berth message append '{"id":"<message-id>","expectedLength":1,"actor":"delivery-owner","action":"closed"}'
|
|
25372
|
+
berth message schema [create|update|append|list]
|
|
25239
25373
|
Messages are stored records. Recipient, type, priority and status are independent.
|
|
25240
25374
|
List includes stored messages; deleting one never deletes its task or blocker.
|
|
25241
25375
|
|