@mp-lb/berth 0.1.0 → 0.1.2

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.
Files changed (2) hide show
  1. package/dist/berth.js +218 -194
  2. 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.0";
5998
+ var version = "0.1.2";
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 { homedir as homedir5, hostname as hostname3 } from "node:os";
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 cwdMatches = [];
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 matches = config2.workMachines.filter(
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,
@@ -23255,9 +23283,142 @@ var messageCreateSchema = external_exports.strictObject({
23255
23283
  body: external_exports.string().trim().min(1).max(8e3)
23256
23284
  });
23257
23285
 
23286
+ // ../../packages/core/src/work/schemas/workerSchema.ts
23287
+ var workerSchema = external_exports.strictObject({
23288
+ threadId: external_exports.string().trim().min(1).max(200).describe("Stable worker thread ID."),
23289
+ host: external_exports.string().trim().min(1).max(200).describe("Host identifier for the worker thread, such as local.")
23290
+ }).describe(
23291
+ "Structured worker destination; an identifier is not authentication or credential approval."
23292
+ );
23293
+
23294
+ // ../../packages/core/src/work/schemas/blockerHandoffReceiptSchema.ts
23295
+ var blockerHandoffReceiptSchema = external_exports.strictObject({
23296
+ updateCount: external_exports.number().int().positive().describe(
23297
+ "Blocker update-log length identifying the original unblocked cycle."
23298
+ ),
23299
+ consumer: slugSchema.describe("Controller that reserved this cycle."),
23300
+ worker: workerSchema,
23301
+ reservedAt: external_exports.iso.datetime().describe("Time the reservation was saved."),
23302
+ delivery: external_exports.strictObject({
23303
+ receiptId: external_exports.string().trim().min(1).max(200).describe("External admitted turn or delivery receipt ID."),
23304
+ confirmedAt: external_exports.iso.datetime().describe("Time delivery was recorded."),
23305
+ sourceUpdateId: external_exports.uuid().optional().describe(
23306
+ "Existing informational update used to import a historical receipt."
23307
+ )
23308
+ }).nullable().describe(
23309
+ "Null means delivery is uncertain or pending; reconcile before sending. A populated delivery must not be resent."
23310
+ )
23311
+ }).describe(
23312
+ "Durable handoff receipt keyed by blocker update cycle; retained when reblocked."
23313
+ );
23314
+
23315
+ // ../../packages/core/src/work/schemas/blockerEventSchema.ts
23316
+ var blockerEventSchema = external_exports.strictObject({
23317
+ state: external_exports.enum(["blocked", "unblocked"]).describe(
23318
+ "blocked is recorded by controller; unblocked is recorded by user."
23319
+ ),
23320
+ notes: external_exports.string().max(4e3).describe("Feedback or resolution notes for this change. Markdown.")
23321
+ }).describe(
23322
+ "An append-only update in the blocker\u2019s embedded log, oldest first."
23323
+ );
23324
+
23258
23325
  // ../../packages/core/src/work/schemas/messageStatusSchema.ts
23259
23326
  var messageStatusSchema = external_exports.enum(["active", "later", "unblocked", "archived"]).describe(
23260
- "Message lifecycle: active, later, unblocked or archived. Factory applies no type-specific transitions."
23327
+ "Message lifecycle: active, later, unblocked or archived. Berth applies no type-specific transitions."
23328
+ );
23329
+
23330
+ // ../../packages/core/src/work/schemas/blockerStatusSchema.ts
23331
+ var blockerStatusSchema = messageStatusSchema.describe(
23332
+ "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."
23333
+ );
23334
+
23335
+ // ../../packages/core/src/work/schemas/blockerBaseSchema.ts
23336
+ var blockerBaseSchema = external_exports.strictObject({
23337
+ id: external_exports.uuid().describe(
23338
+ "Immutable UUID generated by the server when this item is created."
23339
+ ),
23340
+ taskSlug: taskSlugSchema.describe(
23341
+ "The existing task this blocker belongs to."
23342
+ ),
23343
+ status: blockerStatusSchema,
23344
+ updates: external_exports.array(blockerEventSchema).min(1).describe(
23345
+ "Embedded state change log, oldest first. Appended only by state-transition commands."
23346
+ ),
23347
+ handoffs: external_exports.array(blockerHandoffReceiptSchema).default([]).describe(
23348
+ "Durable diagnostics handoffs, one per unblocked update cycle."
23349
+ ),
23350
+ createdAt: external_exports.iso.datetime().nullable().describe(
23351
+ "Creation time in UTC, or null for imported records whose time is unknown."
23352
+ ),
23353
+ completedAt: external_exports.iso.datetime().nullable().describe(
23354
+ "Time the user marked unblocked; null before unblocking or for legacy completions."
23355
+ ),
23356
+ acknowledgedAt: external_exports.iso.datetime().nullable().describe(
23357
+ "Time the consumer acknowledged the item, or null before acknowledgment."
23358
+ ),
23359
+ dismissedAt: external_exports.iso.datetime().nullable().optional().describe(
23360
+ "Time the user hid this pending-review cycle from the web inbox; controller acknowledgment is independent."
23361
+ ),
23362
+ consumer: slugSchema.nullable().describe(
23363
+ "Controller consumer owning this acknowledgment queue item. Set atomically by pull; null before pulling."
23364
+ ),
23365
+ outcome: external_exports.string().max(4e3).describe(
23366
+ "User completion note or legacy resolution; may be empty. Markdown."
23367
+ ),
23368
+ selectedOptionIndex: external_exports.number().int().min(-1).nullable().describe(
23369
+ "Zero-based chosen decision option, or -1 for None of the above. Null for actions or decisions without a recorded choice."
23370
+ )
23371
+ }).describe(
23372
+ "Server-managed identity, queue state and completion receipt for a blocker."
23373
+ );
23374
+
23375
+ // ../../packages/core/src/work/schemas/prioritySchema.ts
23376
+ var prioritySchema = external_exports.enum(["high", "medium", "low", "unset"]).describe("Priority: high, medium, low, or unset when not assigned.");
23377
+
23378
+ // ../../packages/core/src/work/schemas/decisionOptionSchema.ts
23379
+ var decisionOptionSchema = external_exports.strictObject({
23380
+ title: external_exports.string().trim().min(1).max(120).describe("Short name for this choice."),
23381
+ body: external_exports.string().trim().min(1).max(4e3).describe(
23382
+ "Explain this option, its consequences and tradeoffs. Markdown."
23383
+ ),
23384
+ priority: prioritySchema.describe(
23385
+ "Preference for this option: high, medium, low or unset. High is the strongest recommendation."
23386
+ )
23387
+ }).describe("One selectable option in a decision.");
23388
+
23389
+ // ../../packages/core/src/work/schemas/decisionSchema.ts
23390
+ var decisionSchema = external_exports.strictObject({
23391
+ background: external_exports.string().trim().min(1).max(4e3).describe("Context the user needs to understand the decision. Markdown."),
23392
+ problem: external_exports.string().trim().min(1).max(4e3).describe("The problem or question that needs a decision. Markdown."),
23393
+ options: external_exports.array(decisionOptionSchema).max(12).describe(
23394
+ "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."
23395
+ )
23396
+ }).describe("Decision content: background, problem, and prioritized options.");
23397
+
23398
+ // ../../packages/core/src/work/schemas/actionSchema.ts
23399
+ var actionSchema = external_exports.strictObject({
23400
+ background: external_exports.string().trim().min(1).max(4e3).describe("Context explaining why this action is needed. Markdown."),
23401
+ instructions: external_exports.string().trim().min(1).max(6e3).describe(
23402
+ "Concrete instructions for the user to carry out before marking done. Markdown."
23403
+ )
23404
+ }).describe("Action content: background and instructions.");
23405
+
23406
+ // ../../packages/core/src/work/schemas/blockerSchema.ts
23407
+ var blockerSchema = external_exports.discriminatedUnion("type", [
23408
+ blockerBaseSchema.extend({
23409
+ ...decisionSchema.shape,
23410
+ type: external_exports.literal("decision").describe(
23411
+ "A one-time decision. Once submitted its choice is final; create a new decision for a new question."
23412
+ )
23413
+ }),
23414
+ blockerBaseSchema.extend({
23415
+ ...actionSchema.shape,
23416
+ type: external_exports.literal("action").describe(
23417
+ "An action requiring instructions to be carried out. It may be returned with feedback and actioned again."
23418
+ )
23419
+ })
23420
+ ]).describe(
23421
+ "A typed blocker and its queue lifecycle. Completed content is immutable until archived so consumers receive stable results."
23261
23422
  );
23262
23423
 
23263
23424
  // ../../packages/core/src/work/schemas/messageSchema.ts
@@ -23274,7 +23435,8 @@ var messageSchema = external_exports.strictObject({
23274
23435
  status: messageStatusSchema,
23275
23436
  claimedAt: external_exports.iso.datetime().nullable(),
23276
23437
  consumer: external_exports.string().nullable(),
23277
- sourceId: external_exports.string()
23438
+ sourceId: external_exports.string(),
23439
+ blocker: blockerSchema.optional()
23278
23440
  });
23279
23441
 
23280
23442
  // ../../packages/core/src/work/schemas/messageUpdateSchema.ts
@@ -23389,6 +23551,12 @@ var globalConfigSchema = external_exports.strictObject({
23389
23551
  "Shared workspace configuration. An empty workerLinkTemplate disables generated Worker links."
23390
23552
  );
23391
23553
 
23554
+ // ../../packages/core/src/work/schemas/configUpdateSchema.ts
23555
+ var configUpdateSchema = globalConfigSchema.partial().extend({
23556
+ // A missing field in a patch must not apply the read schema's [] default.
23557
+ workMachines: external_exports.array(workMachineSchema).optional()
23558
+ });
23559
+
23392
23560
  // src/importConfiguration.ts
23393
23561
  import { readFile as readFile3 } from "node:fs/promises";
23394
23562
 
@@ -23457,9 +23625,6 @@ var projectReleaseTargetSchema = external_exports.strictObject({
23457
23625
  "One independently released part of a project: its icon, name, and URL."
23458
23626
  );
23459
23627
 
23460
- // ../../packages/core/src/work/schemas/prioritySchema.ts
23461
- var prioritySchema = external_exports.enum(["high", "medium", "low", "unset"]).describe("Priority: high, medium, low, or unset when not assigned.");
23462
-
23463
23628
  // ../../packages/core/src/work/schemas/projectRepoSchema.ts
23464
23629
  var projectRepoSchema = external_exports.strictObject({
23465
23630
  url: linkSchema.refine((url2) => url2.length > 0, "Enter a repo URL."),
@@ -23556,6 +23721,9 @@ var projectSchema = external_exports.object({
23556
23721
  ),
23557
23722
  source: external_exports.string().trim().max(2e3).describe(
23558
23723
  "Where tasks for this project are usually found, such as a workboard. This is project context, not the nullable source on each task."
23724
+ ),
23725
+ notes: external_exports.string().max(2e4).default("").describe(
23726
+ "Free-form Markdown notes about the project. Missing notes read as an empty string."
23559
23727
  )
23560
23728
  }).describe("A project with its identity, work source and useful links.");
23561
23729
 
@@ -23609,14 +23777,14 @@ var runConfigCommand = async (args2) => {
23609
23777
  const [action, input, ...extra] = args2;
23610
23778
  if (!extra.length && action === "schema" && (!input || ["read", "update"].includes(input)))
23611
23779
  return external_exports.toJSONSchema(
23612
- input === "read" ? globalConfigSchema : globalConfigSchema.partial(),
23780
+ input === "read" ? globalConfigSchema : configUpdateSchema,
23613
23781
  { io: "input" }
23614
23782
  );
23615
23783
  if (action === "read" && !input)
23616
23784
  return createWorkClient().config.read.query();
23617
23785
  if (action === "update" && input && !extra.length)
23618
23786
  return createWorkClient().config.update.mutate(
23619
- globalConfigSchema.partial().parse(JSON.parse(input))
23787
+ configUpdateSchema.parse(JSON.parse(input))
23620
23788
  );
23621
23789
  if (action === "import" && input && !extra.length)
23622
23790
  return importConfiguration(input);
@@ -23682,17 +23850,9 @@ var taskSignOffSchema = external_exports.strictObject({
23682
23850
  "User sign-off for a task. Set only through the sign-off endpoint, never through task CRUD."
23683
23851
  );
23684
23852
 
23685
- // ../../packages/core/src/work/schemas/workerSchema.ts
23686
- var workerSchema = external_exports.strictObject({
23687
- threadId: external_exports.string().trim().min(1).max(200).describe("Stable worker thread ID."),
23688
- host: external_exports.string().trim().min(1).max(200).describe("Host identifier for the worker thread, such as local.")
23689
- }).describe(
23690
- "Structured worker destination; an identifier is not authentication or credential approval."
23691
- );
23692
-
23693
23853
  // ../../packages/core/src/work/schemas/terminalWorkerSchema.ts
23694
23854
  var terminalWorkerSchema = external_exports.strictObject({
23695
- sessionId: external_exports.uuid().describe("Factory terminal session UUID."),
23855
+ sessionId: external_exports.uuid().describe("Berth terminal session UUID."),
23696
23856
  machineId: external_exports.uuid().describe("Configured work machine UUID.")
23697
23857
  });
23698
23858
 
@@ -23716,7 +23876,7 @@ var taskSchema = external_exports.strictObject({
23716
23876
  ),
23717
23877
  signOffRequestHistory: external_exports.array(taskSignOffRequestSchema).optional(),
23718
23878
  signOffRequest: taskSignOffRequestSchema.optional().describe(
23719
- "Explicit controller request; Factory does not decide release readiness."
23879
+ "Explicit controller request; Berth does not decide release readiness."
23720
23880
  ),
23721
23881
  signOffRequestedAt: external_exports.iso.datetime().optional().describe("Server-managed time this sign-off cycle entered the inbox."),
23722
23882
  workerRun: external_exports.strictObject({
@@ -23795,7 +23955,7 @@ var promptVariableDefinitions = [
23795
23955
  {
23796
23956
  name: "definitionOfDone",
23797
23957
  optional: false,
23798
- description: "Factory's default task completion guidance."
23958
+ description: "Berth's default task completion guidance."
23799
23959
  },
23800
23960
  {
23801
23961
  name: "documentLocations",
@@ -23910,8 +24070,8 @@ var import_handlebars = __toESM(require_lib(), 1);
23910
24070
  // ../../packages/core/src/work/data/agentPromptDefaults.json
23911
24071
  var agentPromptDefaults_default = {
23912
24072
  workerLaunch: "Run fm read 'worker.md' --store 'mp-lb/roles' and follow its instructions for task {{taskId}}.",
23913
- roleMessage: "You have a new Factory message ({{messageId}}). Read it with berth message read {{messageId}}.",
23914
- reassignedRoleMessage: "Factory message {{messageId}} is ready for you. Read it with berth message read {{messageId}}."
24073
+ roleMessage: "You have a new Berth message ({{messageId}}). Read it with berth message read {{messageId}}.",
24074
+ reassignedRoleMessage: "Berth message {{messageId}} is ready for you. Read it with berth message read {{messageId}}."
23915
24075
  };
23916
24076
 
23917
24077
  // ../../packages/core/src/work/data/defaultAgentPrompts.ts
@@ -24017,21 +24177,6 @@ var prepareTaskWorkerInstance = async (taskId, instanceSlug) => {
24017
24177
  return workerTypeId;
24018
24178
  };
24019
24179
 
24020
- // src/resolveSessionMachine.ts
24021
- import { hostname as hostname5 } from "node:os";
24022
- var resolveSessionMachine = (config2) => {
24023
- const configured = process.env.BERTH_MACHINE_ID;
24024
- const matches = config2.workMachines.filter(
24025
- (item) => configured ? item.id === configured : item.name === hostname5()
24026
- );
24027
- const machine = matches.length === 1 ? matches[0] : !configured && config2.workMachines.length === 1 ? config2.workMachines[0] : void 0;
24028
- if (!machine)
24029
- throw new Error(
24030
- "Work machine is ambiguous. Set BERTH_MACHINE_ID to a configured machine UUID."
24031
- );
24032
- return machine;
24033
- };
24034
-
24035
24180
  // src/tmuxSession.ts
24036
24181
  import { spawnSync } from "node:child_process";
24037
24182
  var tmuxSessionExists = (name) => {
@@ -24439,128 +24584,6 @@ var signOffRequestSchema = external_exports.strictObject({
24439
24584
  )
24440
24585
  });
24441
24586
 
24442
- // ../../packages/core/src/work/schemas/blockerHandoffReceiptSchema.ts
24443
- var blockerHandoffReceiptSchema = external_exports.strictObject({
24444
- updateCount: external_exports.number().int().positive().describe(
24445
- "Blocker update-log length identifying the original unblocked cycle."
24446
- ),
24447
- consumer: slugSchema.describe("Controller that reserved this cycle."),
24448
- worker: workerSchema,
24449
- reservedAt: external_exports.iso.datetime().describe("Time the reservation was saved."),
24450
- delivery: external_exports.strictObject({
24451
- receiptId: external_exports.string().trim().min(1).max(200).describe("External admitted turn or delivery receipt ID."),
24452
- confirmedAt: external_exports.iso.datetime().describe("Time delivery was recorded."),
24453
- sourceUpdateId: external_exports.uuid().optional().describe(
24454
- "Existing informational update used to import a historical receipt."
24455
- )
24456
- }).nullable().describe(
24457
- "Null means delivery is uncertain or pending; reconcile before sending. A populated delivery must not be resent."
24458
- )
24459
- }).describe(
24460
- "Durable handoff receipt keyed by blocker update cycle; retained when reblocked."
24461
- );
24462
-
24463
- // ../../packages/core/src/work/schemas/blockerEventSchema.ts
24464
- var blockerEventSchema = external_exports.strictObject({
24465
- state: external_exports.enum(["blocked", "unblocked"]).describe(
24466
- "blocked is recorded by controller; unblocked is recorded by user."
24467
- ),
24468
- notes: external_exports.string().max(4e3).describe("Feedback or resolution notes for this change. Markdown.")
24469
- }).describe(
24470
- "An append-only update in the blocker\u2019s embedded log, oldest first."
24471
- );
24472
-
24473
- // ../../packages/core/src/work/schemas/blockerStatusSchema.ts
24474
- var blockerStatusSchema = messageStatusSchema.describe(
24475
- "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."
24476
- );
24477
-
24478
- // ../../packages/core/src/work/schemas/blockerBaseSchema.ts
24479
- var blockerBaseSchema = external_exports.strictObject({
24480
- id: external_exports.uuid().describe(
24481
- "Immutable UUID generated by the server when this item is created."
24482
- ),
24483
- taskSlug: taskSlugSchema.describe(
24484
- "The existing task this blocker belongs to."
24485
- ),
24486
- status: blockerStatusSchema,
24487
- updates: external_exports.array(blockerEventSchema).min(1).describe(
24488
- "Embedded state change log, oldest first. Appended only by state-transition commands."
24489
- ),
24490
- handoffs: external_exports.array(blockerHandoffReceiptSchema).default([]).describe(
24491
- "Durable diagnostics handoffs, one per unblocked update cycle."
24492
- ),
24493
- createdAt: external_exports.iso.datetime().nullable().describe(
24494
- "Creation time in UTC, or null for imported records whose time is unknown."
24495
- ),
24496
- completedAt: external_exports.iso.datetime().nullable().describe(
24497
- "Time the user marked unblocked; null before unblocking or for legacy completions."
24498
- ),
24499
- acknowledgedAt: external_exports.iso.datetime().nullable().describe(
24500
- "Time the consumer acknowledged the item, or null before acknowledgment."
24501
- ),
24502
- dismissedAt: external_exports.iso.datetime().nullable().optional().describe(
24503
- "Time the user hid this pending-review cycle from the web inbox; controller acknowledgment is independent."
24504
- ),
24505
- consumer: slugSchema.nullable().describe(
24506
- "Controller consumer owning this acknowledgment queue item. Set atomically by pull; null before pulling."
24507
- ),
24508
- outcome: external_exports.string().max(4e3).describe(
24509
- "User completion note or legacy resolution; may be empty. Markdown."
24510
- ),
24511
- selectedOptionIndex: external_exports.number().int().min(-1).nullable().describe(
24512
- "Zero-based chosen decision option, or -1 for None of the above. Null for actions or decisions without a recorded choice."
24513
- )
24514
- }).describe(
24515
- "Server-managed identity, queue state and completion receipt for a blocker."
24516
- );
24517
-
24518
- // ../../packages/core/src/work/schemas/decisionOptionSchema.ts
24519
- var decisionOptionSchema = external_exports.strictObject({
24520
- title: external_exports.string().trim().min(1).max(120).describe("Short name for this choice."),
24521
- body: external_exports.string().trim().min(1).max(4e3).describe(
24522
- "Explain this option, its consequences and tradeoffs. Markdown."
24523
- ),
24524
- priority: prioritySchema.describe(
24525
- "Preference for this option: high, medium, low or unset. High is the strongest recommendation."
24526
- )
24527
- }).describe("One selectable option in a decision.");
24528
-
24529
- // ../../packages/core/src/work/schemas/decisionSchema.ts
24530
- var decisionSchema = external_exports.strictObject({
24531
- background: external_exports.string().trim().min(1).max(4e3).describe("Context the user needs to understand the decision. Markdown."),
24532
- problem: external_exports.string().trim().min(1).max(4e3).describe("The problem or question that needs a decision. Markdown."),
24533
- options: external_exports.array(decisionOptionSchema).max(12).describe(
24534
- "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."
24535
- )
24536
- }).describe("Decision content: background, problem, and prioritized options.");
24537
-
24538
- // ../../packages/core/src/work/schemas/actionSchema.ts
24539
- var actionSchema = external_exports.strictObject({
24540
- background: external_exports.string().trim().min(1).max(4e3).describe("Context explaining why this action is needed. Markdown."),
24541
- instructions: external_exports.string().trim().min(1).max(6e3).describe(
24542
- "Concrete instructions for the user to carry out before marking done. Markdown."
24543
- )
24544
- }).describe("Action content: background and instructions.");
24545
-
24546
- // ../../packages/core/src/work/schemas/blockerSchema.ts
24547
- var blockerSchema = external_exports.discriminatedUnion("type", [
24548
- blockerBaseSchema.extend({
24549
- ...decisionSchema.shape,
24550
- type: external_exports.literal("decision").describe(
24551
- "A one-time decision. Once submitted its choice is final; create a new decision for a new question."
24552
- )
24553
- }),
24554
- blockerBaseSchema.extend({
24555
- ...actionSchema.shape,
24556
- type: external_exports.literal("action").describe(
24557
- "An action requiring instructions to be carried out. It may be returned with feedback and actioned again."
24558
- )
24559
- })
24560
- ]).describe(
24561
- "A typed blocker and its queue lifecycle. Completed content is immutable until archived so consumers receive stable results."
24562
- );
24563
-
24564
24587
  // ../../packages/core/src/work/schemas/blockerPollSchema.ts
24565
24588
  var blockerPollSchema = external_exports.strictObject({
24566
24589
  id: external_exports.uuid(),
@@ -24676,7 +24699,8 @@ var taskColumnMoveSchema = external_exports.strictObject({
24676
24699
  // ../../packages/core/src/work/schemas/projectUpdateSchema.ts
24677
24700
  var projectUpdateSchema = projectSchema.omit({ uuid: true, lastWorkedOnAt: true }).partial().extend({
24678
24701
  tags: external_exports.array(external_exports.string().trim().min(1).max(100)).optional(),
24679
- company: external_exports.string().trim().max(100).optional()
24702
+ company: external_exports.string().trim().max(100).optional(),
24703
+ notes: external_exports.string().max(2e4).optional()
24680
24704
  }).required({ id: true }).describe(
24681
24705
  "Edit an existing project by id. Only supplied fields change. Both user and controller can edit projects."
24682
24706
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mp-lb/berth",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {