@warmhub/cli 0.84.3 → 0.86.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.
Files changed (2) hide show
  1. package/dist/wh.js +774 -561
  2. package/package.json +1 -1
package/dist/wh.js CHANGED
@@ -4092,11 +4092,11 @@ var require_util2 = __commonJS((exports, module) => {
4092
4092
  var { isUint8Array } = __require("node:util/types");
4093
4093
  var { webidl } = require_webidl();
4094
4094
  var supportedHashes = [];
4095
- var crypto2;
4095
+ var crypto;
4096
4096
  try {
4097
- crypto2 = __require("node:crypto");
4097
+ crypto = __require("node:crypto");
4098
4098
  const possibleRelevantHashes = ["sha256", "sha384", "sha512"];
4099
- supportedHashes = crypto2.getHashes().filter((hash) => possibleRelevantHashes.includes(hash));
4099
+ supportedHashes = crypto.getHashes().filter((hash) => possibleRelevantHashes.includes(hash));
4100
4100
  } catch {}
4101
4101
  function responseURL(response) {
4102
4102
  const urlList = response.urlList;
@@ -4355,7 +4355,7 @@ var require_util2 = __commonJS((exports, module) => {
4355
4355
  }
4356
4356
  }
4357
4357
  function bytesMatch(bytes, metadataList) {
4358
- if (crypto2 === undefined) {
4358
+ if (crypto === undefined) {
4359
4359
  return true;
4360
4360
  }
4361
4361
  const parsedMetadata = parseMetadata(metadataList);
@@ -4370,7 +4370,7 @@ var require_util2 = __commonJS((exports, module) => {
4370
4370
  for (const item of metadata) {
4371
4371
  const algorithm = item.algo;
4372
4372
  const expectedValue = item.hash;
4373
- let actualValue = crypto2.createHash(algorithm).update(bytes).digest("base64");
4373
+ let actualValue = crypto.createHash(algorithm).update(bytes).digest("base64");
4374
4374
  if (actualValue[actualValue.length - 1] === "=") {
4375
4375
  if (actualValue[actualValue.length - 2] === "=") {
4376
4376
  actualValue = actualValue.slice(0, -2);
@@ -5355,8 +5355,8 @@ var require_body = __commonJS((exports, module) => {
5355
5355
  var { multipartFormDataParser } = require_formdata_parser();
5356
5356
  var random;
5357
5357
  try {
5358
- const crypto2 = __require("node:crypto");
5359
- random = (max) => crypto2.randomInt(0, max);
5358
+ const crypto = __require("node:crypto");
5359
+ random = (max) => crypto.randomInt(0, max);
5360
5360
  } catch {
5361
5361
  random = (max) => Math.floor(Math.random(max));
5362
5362
  }
@@ -16107,13 +16107,13 @@ var require_util7 = __commonJS((exports, module) => {
16107
16107
  var require_frame = __commonJS((exports, module) => {
16108
16108
  var { maxUnsigned16Bit } = require_constants5();
16109
16109
  var BUFFER_SIZE = 16386;
16110
- var crypto2;
16110
+ var crypto;
16111
16111
  var buffer = null;
16112
16112
  var bufIdx = BUFFER_SIZE;
16113
16113
  try {
16114
- crypto2 = __require("node:crypto");
16114
+ crypto = __require("node:crypto");
16115
16115
  } catch {
16116
- crypto2 = {
16116
+ crypto = {
16117
16117
  randomFillSync: function randomFillSync(buffer2, _offset, _size) {
16118
16118
  for (let i = 0;i < buffer2.length; ++i) {
16119
16119
  buffer2[i] = Math.random() * 255 | 0;
@@ -16125,7 +16125,7 @@ var require_frame = __commonJS((exports, module) => {
16125
16125
  function generateMask() {
16126
16126
  if (bufIdx === BUFFER_SIZE) {
16127
16127
  bufIdx = 0;
16128
- crypto2.randomFillSync(buffer ??= Buffer.allocUnsafe(BUFFER_SIZE), 0, BUFFER_SIZE);
16128
+ crypto.randomFillSync(buffer ??= Buffer.allocUnsafe(BUFFER_SIZE), 0, BUFFER_SIZE);
16129
16129
  }
16130
16130
  return [buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++]];
16131
16131
  }
@@ -16193,9 +16193,9 @@ var require_connection = __commonJS((exports, module) => {
16193
16193
  var { Headers: Headers2, getHeadersList } = require_headers();
16194
16194
  var { getDecodeSplit } = require_util2();
16195
16195
  var { WebsocketFrameSend } = require_frame();
16196
- var crypto2;
16196
+ var crypto;
16197
16197
  try {
16198
- crypto2 = __require("node:crypto");
16198
+ crypto = __require("node:crypto");
16199
16199
  } catch {}
16200
16200
  function establishWebSocketConnection(url, protocols, client, ws, onEstablish, options) {
16201
16201
  const requestURL = url;
@@ -16214,7 +16214,7 @@ var require_connection = __commonJS((exports, module) => {
16214
16214
  const headersList = getHeadersList(new Headers2(options.headers));
16215
16215
  request.headersList = headersList;
16216
16216
  }
16217
- const keyValue = crypto2.randomBytes(16).toString("base64");
16217
+ const keyValue = crypto.randomBytes(16).toString("base64");
16218
16218
  request.headersList.append("sec-websocket-key", keyValue);
16219
16219
  request.headersList.append("sec-websocket-version", "13");
16220
16220
  for (const protocol of protocols) {
@@ -16244,7 +16244,7 @@ var require_connection = __commonJS((exports, module) => {
16244
16244
  return;
16245
16245
  }
16246
16246
  const secWSAccept = response.headersList.get("Sec-WebSocket-Accept");
16247
- const digest2 = crypto2.createHash("sha1").update(keyValue + uid).digest("base64");
16247
+ const digest2 = crypto.createHash("sha1").update(keyValue + uid).digest("base64");
16248
16248
  if (secWSAccept !== digest2) {
16249
16249
  failWebsocketConnection(ws, "Incorrect hash received in Sec-WebSocket-Accept header.");
16250
16250
  return;
@@ -19146,31 +19146,6 @@ var import_usingCtx = __toESM(require_usingCtx(), 1);
19146
19146
  var import_awaitAsyncGenerator = __toESM(require_awaitAsyncGenerator(), 1);
19147
19147
  var import_wrapAsyncGenerator = __toESM(require_wrapAsyncGenerator(), 1);
19148
19148
  var import_objectSpread29 = __toESM(require_objectSpread2(), 1);
19149
- // ../../packages/rules/src/authority-coverage.ts
19150
- var EMPTY_COVERAGE = Object.freeze({
19151
- include: Object.freeze([])
19152
- });
19153
- var FULL_REPO_COVERAGE = Object.freeze({
19154
- include: Object.freeze(["**"])
19155
- });
19156
- var EMPTY_COVERAGE_SET = Object.freeze([]);
19157
- var FULL_REPO_COVERAGE_SET = Object.freeze([
19158
- Object.freeze([FULL_REPO_COVERAGE])
19159
- ]);
19160
- // ../../packages/rules/src/stable-json.ts
19161
- function stableJson(value) {
19162
- if (!value || typeof value !== "object") {
19163
- return JSON.stringify(value);
19164
- }
19165
- if (Array.isArray(value)) {
19166
- return `[${value.map((entry) => stableJson(entry)).join(",")}]`;
19167
- }
19168
- const record = value;
19169
- return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(",")}}`;
19170
- }
19171
- function stableJsonEquals(left, right) {
19172
- return stableJson(left) === stableJson(right);
19173
- }
19174
19149
  // ../../packages/rules/src/builtin-shapes.ts
19175
19150
  var BUILTIN_SHAPE_NAMES = [
19176
19151
  "Arc",
@@ -19399,6 +19374,21 @@ function resolveComponentTemplate(value, ctx) {
19399
19374
  });
19400
19375
  }
19401
19376
 
19377
+ // ../../packages/rules/src/stable-json.ts
19378
+ function stableJson(value) {
19379
+ if (!value || typeof value !== "object") {
19380
+ return JSON.stringify(value);
19381
+ }
19382
+ if (Array.isArray(value)) {
19383
+ return `[${value.map((entry) => stableJson(entry)).join(",")}]`;
19384
+ }
19385
+ const record = value;
19386
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(",")}}`;
19387
+ }
19388
+ function stableJsonEquals(left, right) {
19389
+ return stableJson(left) === stableJson(right);
19390
+ }
19391
+
19402
19392
  // ../../packages/rules/src/org-activity-events.ts
19403
19393
  var ORG_MEMBER_ADDED_EVENT_TYPE = "org.member_added";
19404
19394
  var ORG_REPO_CREATED_EVENT_TYPE = "org.repo_created";
@@ -20410,7 +20400,304 @@ function joinFieldIdentityPath(...segments) {
20410
20400
  function joinFieldPathWithEscaper(escapeSegment, segments) {
20411
20401
  return segments.map((segment) => typeof segment === "number" ? `[${segment}]` : escapeSegment(segment)).join(".").replace(/\.\[/g, "[");
20412
20402
  }
20403
+ // ../../packages/rules/src/tokens.ts
20404
+ var COMMIT_TOKEN_SYNTAX_REMOVED_MESSAGE = "$N/#N commit-token syntax is no longer supported. Use explicit names and explicit wrefs. For assertions about newly created things, create the thing with a deterministic name and set about to that wref in the same commit.";
20405
+ var ANY_TOKEN_RE = /[$#]\d+/;
20406
+ function hasAnyTokens(s) {
20407
+ return ANY_TOKEN_RE.test(s);
20408
+ }
20413
20409
 
20410
+ // ../../packages/rules/src/preflight-commit.ts
20411
+ function preflightCommitDiagnostics(operations, options) {
20412
+ const errors = [];
20413
+ rejectCommitTokenSyntax(operations, errors, options);
20414
+ illegalOpSequences(operations, errors, options?.checkAddAdd ?? true, options);
20415
+ return errors;
20416
+ }
20417
+ function sourceOperationIndex(filteredIndex, options) {
20418
+ return options?.sourceOperationIndexes?.[filteredIndex] ?? filteredIndex;
20419
+ }
20420
+ function getOpName(op) {
20421
+ return op.name;
20422
+ }
20423
+ function tokenStringFields(op) {
20424
+ const fields = [getOpName(op), op.newName];
20425
+ if (typeof op.about === "string") {
20426
+ fields.push(op.about);
20427
+ }
20428
+ if (op.members) {
20429
+ fields.push(...op.members);
20430
+ }
20431
+ return fields;
20432
+ }
20433
+ function rejectCommitTokenSyntax(operations, errors, options) {
20434
+ for (let i = 0;i < operations.length; i++) {
20435
+ const op = operations[i];
20436
+ if (!op)
20437
+ continue;
20438
+ for (const field of tokenStringFields(op)) {
20439
+ if (field && hasAnyTokens(field)) {
20440
+ errors.push({
20441
+ code: "COMMIT_TOKEN_SYNTAX_REMOVED",
20442
+ operationIndex: sourceOperationIndex(i, options),
20443
+ message: COMMIT_TOKEN_SYNTAX_REMOVED_MESSAGE
20444
+ });
20445
+ break;
20446
+ }
20447
+ }
20448
+ }
20449
+ }
20450
+ function illegalOpSequences(operations, errors, checkAddAdd, options) {
20451
+ const opHistory = new Map;
20452
+ for (let i = 0;i < operations.length; i++) {
20453
+ const op = operations[i];
20454
+ if (!op)
20455
+ continue;
20456
+ const name = getOpName(op);
20457
+ if (!name)
20458
+ continue;
20459
+ if (hasAnyTokens(name))
20460
+ continue;
20461
+ const kind = inferOperationKind({ ...op, name });
20462
+ const qualName = kind === "shape" ? `shape:${name}` : `thing:${name}`;
20463
+ const history = opHistory.get(qualName) ?? [];
20464
+ history.push({
20465
+ operation: op.operation,
20466
+ index: sourceOperationIndex(i, options)
20467
+ });
20468
+ opHistory.set(qualName, history);
20469
+ }
20470
+ for (const [qualName, history] of opHistory) {
20471
+ if (history.length < 2)
20472
+ continue;
20473
+ for (let i = 1;i < history.length; i++) {
20474
+ const prev = history[i - 1];
20475
+ const curr = history[i];
20476
+ if (!prev || !curr)
20477
+ continue;
20478
+ const pair = `${prev.operation}+${curr.operation}`;
20479
+ if (checkAddAdd && pair === "add+add") {
20480
+ errors.push({
20481
+ code: "ILLEGAL_OP_SEQUENCE",
20482
+ operationIndex: curr.index,
20483
+ message: `Cannot add "${qualName}" twice in the same commit`
20484
+ });
20485
+ }
20486
+ if (pair === "revise+add") {
20487
+ errors.push({
20488
+ code: "ILLEGAL_OP_SEQUENCE",
20489
+ operationIndex: curr.index,
20490
+ message: `Cannot revise then add "${qualName}" in the same commit`
20491
+ });
20492
+ }
20493
+ }
20494
+ }
20495
+ }
20496
+
20497
+ // ../../packages/rules/src/preflight-operation.ts
20498
+ var collectionTypes = ["arc", "bond", "set", "list", "pair"];
20499
+ var canonicalCollectionTypes = ["arc", "bond", "set", "list"];
20500
+ var collectionOps = ["add", "revise"];
20501
+ var COLLECTION_CREATE_REQUIRES_NAME_MESSAGE = "Collection create requires a name. Collections are ordinary named things (ADR 0004).";
20502
+ var COLLECTION_ABOUT_REMOVED_MESSAGE = 'about accepts a wref. Create the collection as its own named operation, then point the assertion at it. Prefer deterministic relationship names, for example: [{"operation":"add","kind":"collection","type":"arc","name":"a-to-b","members":["A","B"]},{"operation":"add","kind":"assertion","about":"Arc/a-to-b","name":"Assertion/example","data":{}}]. For CLI usage, use wh commit submit --file with the two operations.';
20503
+ function preflightOpDiagnostics(op, operationIndex) {
20504
+ const errors = [];
20505
+ errors.push(...builtinShapeGuard(op, operationIndex));
20506
+ errors.push(...contentNameGuard(op, operationIndex));
20507
+ errors.push(...plusSignGuard(op, operationIndex));
20508
+ errors.push(...validateCollectionAbouts(op, operationIndex));
20509
+ errors.push(...validateCollectionOps(op, operationIndex));
20510
+ return errors;
20511
+ }
20512
+ function builtinShapeGuard(op, operationIndex) {
20513
+ const errors = [];
20514
+ const name = op.name;
20515
+ const isShapeRename = op.operation === "rename" && (op.kind === "shape" || op.name !== undefined && !splitLocalPath(op.name));
20516
+ const reservedSourcePassThrough = isShapeRename && name !== undefined && isRenameableSquattedShape(name) && op.newName !== undefined && !isReservedCollectionShape(op.newName) && !isBuiltinShape(op.newName);
20517
+ if (name && isRetiredCollectionShape(name) && !reservedSourcePassThrough) {
20518
+ errors.push({
20519
+ code: "RESERVED_NAME",
20520
+ operationIndex,
20521
+ message: `Shape "${name}" is a retired collection shape and cannot be written manually`
20522
+ });
20523
+ }
20524
+ if (op.operation === "rename" && (op.kind === "shape" || op.name !== undefined && !splitLocalPath(op.name)) && op.newName && isRetiredCollectionShape(op.newName)) {
20525
+ errors.push({
20526
+ code: "RESERVED_NAME",
20527
+ operationIndex,
20528
+ message: `Shape "${op.newName}" is a retired collection shape and cannot be written manually`
20529
+ });
20530
+ } else {
20531
+ const builtinShapeName = name && isBuiltinShape(name) && !reservedSourcePassThrough ? name : isShapeRename && op.newName && isBuiltinShape(op.newName) ? op.newName : undefined;
20532
+ if (builtinShapeName && (isShapeRename || op.operation !== "retract" && op.kind === "shape")) {
20533
+ const action = op.operation === "add" ? "created" : op.operation === "rename" ? "renamed" : "revised";
20534
+ errors.push({
20535
+ code: "RESERVED_NAME",
20536
+ operationIndex,
20537
+ message: `Shape "${builtinShapeName}" is a built-in shape and cannot be ${action} manually`
20538
+ });
20539
+ }
20540
+ }
20541
+ if (name) {
20542
+ const local = splitLocalPath(name);
20543
+ if (local && isRetiredCollectionShape(local.shapePrefix)) {
20544
+ errors.push({
20545
+ code: "VALIDATION_ERROR",
20546
+ operationIndex,
20547
+ message: `Cannot ${op.operation} under retired collection shape "${local.shapePrefix}". Triple is read-only and retired for new collection writes.`
20548
+ });
20549
+ } else if (op.kind === "thing" && local && isBuiltinCollectionShape(local.shapePrefix)) {
20550
+ const message = op.operation === "add" ? `Cannot add kind="thing" under built-in shape "${local.shapePrefix}". Use kind="collection" instead.` : `Cannot revise kind="thing" under built-in shape "${local.shapePrefix}". Use kind="collection" to revise a collection.`;
20551
+ errors.push({
20552
+ code: "VALIDATION_ERROR",
20553
+ operationIndex,
20554
+ message
20555
+ });
20556
+ }
20557
+ }
20558
+ return errors;
20559
+ }
20560
+ function contentNameGuard(op, operationIndex) {
20561
+ if (op.kind !== "thing" || !op.name)
20562
+ return [];
20563
+ const local = splitLocalPath(op.name);
20564
+ if (!local || !isContentShape(local.shapePrefix))
20565
+ return [];
20566
+ const bareName = local.bareName;
20567
+ if (isSynthesizedContentName(bareName)) {
20568
+ return [
20569
+ {
20570
+ code: "READ_ONLY_BUILTIN_CONTENT",
20571
+ operationIndex,
20572
+ message: `${local.shapePrefix}/${bareName} is synthesized; writes are not allowed`
20573
+ }
20574
+ ];
20575
+ }
20576
+ if (!isStoredContentName(bareName)) {
20577
+ return [
20578
+ {
20579
+ code: "UNKNOWN_CONTENT_NAME",
20580
+ operationIndex,
20581
+ message: `${local.shapePrefix}/${bareName} is not a recognized well-known content name`
20582
+ }
20583
+ ];
20584
+ }
20585
+ return [];
20586
+ }
20587
+ function plusSignGuard(op, operationIndex) {
20588
+ const errors = [];
20589
+ const name = op.name;
20590
+ if (op.newName?.includes("+")) {
20591
+ errors.push({
20592
+ code: "VALIDATION_ERROR",
20593
+ operationIndex,
20594
+ message: `Name "${op.newName}" contains reserved character "+". The "+" character is reserved for the historical collection auto-name namespace and is not allowed in user-supplied names.`
20595
+ });
20596
+ return errors;
20597
+ }
20598
+ if (name?.includes("+")) {
20599
+ if (op.kind === "collection" && op.operation === "add") {
20600
+ errors.push({
20601
+ code: "VALIDATION_ERROR",
20602
+ operationIndex,
20603
+ message: `Name "${name}" contains reserved character "+". The "+" character is reserved for the historical collection auto-name namespace and is not allowed in user-supplied names.`
20604
+ });
20605
+ return errors;
20606
+ }
20607
+ if (op.kind === "collection")
20608
+ return errors;
20609
+ const local = splitLocalPath(name);
20610
+ if (local && isReservedCollectionShape(local.shapePrefix))
20611
+ return errors;
20612
+ errors.push({
20613
+ code: "VALIDATION_ERROR",
20614
+ operationIndex,
20615
+ message: `Name "${name}" contains reserved character "+". The "+" character is reserved for the historical collection auto-name namespace and is not allowed in user-supplied names.`
20616
+ });
20617
+ }
20618
+ return errors;
20619
+ }
20620
+ function validateCollectionAbouts(op, operationIndex) {
20621
+ const errors = [];
20622
+ if (!op.about || typeof op.about === "string")
20623
+ return errors;
20624
+ errors.push({
20625
+ code: "VALIDATION_ERROR",
20626
+ operationIndex,
20627
+ message: COLLECTION_ABOUT_REMOVED_MESSAGE
20628
+ });
20629
+ return errors;
20630
+ }
20631
+ function validateCollectionOps(op, operationIndex) {
20632
+ const errors = [];
20633
+ if (op.kind !== "collection")
20634
+ return errors;
20635
+ if (!collectionOps.includes(op.operation)) {
20636
+ return errors;
20637
+ }
20638
+ if (op.operation === "add" && !op.name) {
20639
+ errors.push({
20640
+ code: "VALIDATION_ERROR",
20641
+ operationIndex,
20642
+ message: COLLECTION_CREATE_REQUIRES_NAME_MESSAGE
20643
+ });
20644
+ }
20645
+ if (!op.type || !collectionTypes.includes(op.type)) {
20646
+ errors.push({
20647
+ code: "VALIDATION_ERROR",
20648
+ operationIndex,
20649
+ message: `Collection "type" is invalid. Use one of: ${canonicalCollectionTypes.join(", ")}. Got: "${op.type ?? ""}"`
20650
+ });
20651
+ }
20652
+ if (!op.members || !Array.isArray(op.members)) {
20653
+ errors.push({
20654
+ code: "VALIDATION_ERROR",
20655
+ operationIndex,
20656
+ message: 'Collection requires a "members" array'
20657
+ });
20658
+ } else {
20659
+ for (let i = 0;i < op.members.length; i++) {
20660
+ if (typeof op.members[i] !== "string") {
20661
+ errors.push({
20662
+ code: "VALIDATION_ERROR",
20663
+ operationIndex,
20664
+ message: `Collection member at index ${i} must be a string`
20665
+ });
20666
+ }
20667
+ }
20668
+ }
20669
+ if (op.type && op.members) {
20670
+ const arityError = collectionArityError(op.type, op.members);
20671
+ if (arityError) {
20672
+ errors.push({
20673
+ code: "VALIDATION_ERROR",
20674
+ operationIndex,
20675
+ message: arityError
20676
+ });
20677
+ }
20678
+ }
20679
+ return errors;
20680
+ }
20681
+ function collectionArityError(tag, members) {
20682
+ switch (tag) {
20683
+ case "arc":
20684
+ return members.length !== 2 ? `Arc requires exactly 2 members, got ${members.length}` : null;
20685
+ case "bond":
20686
+ return members.length !== 2 ? `Bond requires exactly 2 members, got ${members.length}` : null;
20687
+ case "pair":
20688
+ return members.length !== 2 ? `Pair requires exactly 2 members, got ${members.length}` : null;
20689
+ case "set":
20690
+ case "list":
20691
+ return members.length < 1 ? `${tag === "set" ? "Set" : "List"} requires at least 1 member, got 0` : null;
20692
+ }
20693
+ }
20694
+ // ../../packages/rules/src/preflight.ts
20695
+ function preflightOpDiagnostics2(op, operationIndex) {
20696
+ return preflightOpDiagnostics(op, operationIndex);
20697
+ }
20698
+ function preflightCommitDiagnostics2(operations, options) {
20699
+ return preflightCommitDiagnostics(operations, options);
20700
+ }
20414
20701
  // ../../packages/rules/src/shape-types.ts
20415
20702
  var BASE_PRIMITIVE_TYPES = [
20416
20703
  "number",
@@ -20608,6 +20895,14 @@ function displayFieldEntry(fieldType) {
20608
20895
  return { type, description };
20609
20896
  return type;
20610
20897
  }
20898
+ // ../../packages/rules/src/shape-field-key.ts
20899
+ function foldFieldName(name) {
20900
+ const stripped = name.endsWith("?") ? name.slice(0, -1) : name;
20901
+ return stripped.toLowerCase();
20902
+ }
20903
+ function foldFieldPath(path) {
20904
+ return path.split(".").map((segment) => foldFieldName(segment)).join(".");
20905
+ }
20611
20906
 
20612
20907
  // ../../node_modules/.bun/re2js@2.8.3/node_modules/re2js/build/index.js
20613
20908
  /*!
@@ -27102,372 +27397,7 @@ function normalizeOptionalTypeSpec(spec) {
27102
27397
  }
27103
27398
  return { spec, optionalByType: false };
27104
27399
  }
27105
- // ../../packages/rules/src/external-contract.ts
27106
- var FORBIDDEN_EXTERNAL_FIELDS = new Set([
27107
- "_id",
27108
- "_creationTime",
27109
- "repoId",
27110
- "orgId",
27111
- "thingId",
27112
- "shapeThingId",
27113
- "aboutThingId",
27114
- "currentVersionId",
27115
- "createdInCommitId",
27116
- "validatedShapeVersionId"
27117
- ]);
27118
- // ../../packages/rules/src/name-charset.ts
27119
- var RESERVED_NAME_SEGMENT_CHARS = ["?", "#", "@", ":", "$"];
27120
- function escapeForCharClass(char) {
27121
- return char.replace(/[\\\]^-]/g, "\\$&");
27122
- }
27123
- var RESERVED_CLASS_BODY = RESERVED_NAME_SEGMENT_CHARS.map(escapeForCharClass).join("");
27124
- var NAME_SEGMENT_FORBIDDEN_RE = new RegExp(`[${RESERVED_CLASS_BODY}\\s]`);
27125
- var PATH_SEGMENT_FORBIDDEN_RE = new RegExp(`[/${RESERVED_CLASS_BODY}\\s]`);
27126
- // ../../packages/rules/src/permission-scopes.ts
27127
- var PUBLIC_ORG_PERMISSIONS = new Set([
27128
- "org:read",
27129
- "components:read"
27130
- ]);
27131
- // ../../packages/rules/src/tokens.ts
27132
- var COMMIT_TOKEN_SYNTAX_REMOVED_MESSAGE = "$N/#N commit-token syntax is no longer supported. Use explicit names and explicit wrefs. For assertions about newly created things, create the thing with a deterministic name and set about to that wref in the same commit.";
27133
- var ANY_TOKEN_RE = /[$#]\d+/;
27134
- function hasAnyTokens(s) {
27135
- return ANY_TOKEN_RE.test(s);
27136
- }
27137
-
27138
- // ../../packages/rules/src/preflight-commit.ts
27139
- function preflightCommitDiagnostics(operations, options) {
27140
- const errors = [];
27141
- rejectCommitTokenSyntax(operations, errors, options);
27142
- illegalOpSequences(operations, errors, options?.checkAddAdd ?? true, options);
27143
- return errors;
27144
- }
27145
- function sourceOperationIndex(filteredIndex, options) {
27146
- return options?.sourceOperationIndexes?.[filteredIndex] ?? filteredIndex;
27147
- }
27148
- function getOpName(op) {
27149
- return op.name;
27150
- }
27151
- function tokenStringFields(op) {
27152
- const fields = [getOpName(op), op.newName];
27153
- if (typeof op.about === "string") {
27154
- fields.push(op.about);
27155
- }
27156
- if (op.members) {
27157
- fields.push(...op.members);
27158
- }
27159
- return fields;
27160
- }
27161
- function rejectCommitTokenSyntax(operations, errors, options) {
27162
- for (let i = 0;i < operations.length; i++) {
27163
- const op = operations[i];
27164
- if (!op)
27165
- continue;
27166
- for (const field of tokenStringFields(op)) {
27167
- if (field && hasAnyTokens(field)) {
27168
- errors.push({
27169
- code: "COMMIT_TOKEN_SYNTAX_REMOVED",
27170
- operationIndex: sourceOperationIndex(i, options),
27171
- message: COMMIT_TOKEN_SYNTAX_REMOVED_MESSAGE
27172
- });
27173
- break;
27174
- }
27175
- }
27176
- }
27177
- }
27178
- function illegalOpSequences(operations, errors, checkAddAdd, options) {
27179
- const opHistory = new Map;
27180
- for (let i = 0;i < operations.length; i++) {
27181
- const op = operations[i];
27182
- if (!op)
27183
- continue;
27184
- const name = getOpName(op);
27185
- if (!name)
27186
- continue;
27187
- if (hasAnyTokens(name))
27188
- continue;
27189
- const kind = inferOperationKind({ ...op, name });
27190
- const qualName = kind === "shape" ? `shape:${name}` : `thing:${name}`;
27191
- const history = opHistory.get(qualName) ?? [];
27192
- history.push({
27193
- operation: op.operation,
27194
- index: sourceOperationIndex(i, options)
27195
- });
27196
- opHistory.set(qualName, history);
27197
- }
27198
- for (const [qualName, history] of opHistory) {
27199
- if (history.length < 2)
27200
- continue;
27201
- for (let i = 1;i < history.length; i++) {
27202
- const prev = history[i - 1];
27203
- const curr = history[i];
27204
- if (!prev || !curr)
27205
- continue;
27206
- const pair = `${prev.operation}+${curr.operation}`;
27207
- if (checkAddAdd && pair === "add+add") {
27208
- errors.push({
27209
- code: "ILLEGAL_OP_SEQUENCE",
27210
- operationIndex: curr.index,
27211
- message: `Cannot add "${qualName}" twice in the same commit`
27212
- });
27213
- }
27214
- if (pair === "revise+add") {
27215
- errors.push({
27216
- code: "ILLEGAL_OP_SEQUENCE",
27217
- operationIndex: curr.index,
27218
- message: `Cannot revise then add "${qualName}" in the same commit`
27219
- });
27220
- }
27221
- }
27222
- }
27223
- }
27224
27400
 
27225
- // ../../packages/rules/src/preflight-operation.ts
27226
- var collectionTypes = ["arc", "bond", "set", "list", "pair"];
27227
- var canonicalCollectionTypes = ["arc", "bond", "set", "list"];
27228
- var collectionOps = ["add", "revise"];
27229
- var COLLECTION_CREATE_REQUIRES_NAME_MESSAGE = "Collection create requires a name. Collections are ordinary named things (ADR 0004).";
27230
- var COLLECTION_ABOUT_REMOVED_MESSAGE = 'about accepts a wref. Create the collection as its own named operation, then point the assertion at it. Prefer deterministic relationship names, for example: [{"operation":"add","kind":"collection","type":"arc","name":"a-to-b","members":["A","B"]},{"operation":"add","kind":"assertion","about":"Arc/a-to-b","name":"Assertion/example","data":{}}]. For CLI usage, use wh commit submit --file with the two operations.';
27231
- function preflightOpDiagnostics(op, operationIndex) {
27232
- const errors = [];
27233
- errors.push(...builtinShapeGuard(op, operationIndex));
27234
- errors.push(...contentNameGuard(op, operationIndex));
27235
- errors.push(...plusSignGuard(op, operationIndex));
27236
- errors.push(...validateCollectionAbouts(op, operationIndex));
27237
- errors.push(...validateCollectionOps(op, operationIndex));
27238
- return errors;
27239
- }
27240
- function builtinShapeGuard(op, operationIndex) {
27241
- const errors = [];
27242
- const name = op.name;
27243
- const isShapeRename = op.operation === "rename" && (op.kind === "shape" || op.name !== undefined && !splitLocalPath(op.name));
27244
- const reservedSourcePassThrough = isShapeRename && name !== undefined && isRenameableSquattedShape(name) && op.newName !== undefined && !isReservedCollectionShape(op.newName) && !isBuiltinShape(op.newName);
27245
- if (name && isRetiredCollectionShape(name) && !reservedSourcePassThrough) {
27246
- errors.push({
27247
- code: "RESERVED_NAME",
27248
- operationIndex,
27249
- message: `Shape "${name}" is a retired collection shape and cannot be written manually`
27250
- });
27251
- }
27252
- if (op.operation === "rename" && (op.kind === "shape" || op.name !== undefined && !splitLocalPath(op.name)) && op.newName && isRetiredCollectionShape(op.newName)) {
27253
- errors.push({
27254
- code: "RESERVED_NAME",
27255
- operationIndex,
27256
- message: `Shape "${op.newName}" is a retired collection shape and cannot be written manually`
27257
- });
27258
- } else {
27259
- const builtinShapeName = name && isBuiltinShape(name) && !reservedSourcePassThrough ? name : isShapeRename && op.newName && isBuiltinShape(op.newName) ? op.newName : undefined;
27260
- if (builtinShapeName && (isShapeRename || op.operation !== "retract" && op.kind === "shape")) {
27261
- const action = op.operation === "add" ? "created" : op.operation === "rename" ? "renamed" : "revised";
27262
- errors.push({
27263
- code: "RESERVED_NAME",
27264
- operationIndex,
27265
- message: `Shape "${builtinShapeName}" is a built-in shape and cannot be ${action} manually`
27266
- });
27267
- }
27268
- }
27269
- if (name) {
27270
- const local = splitLocalPath(name);
27271
- if (local && isRetiredCollectionShape(local.shapePrefix)) {
27272
- errors.push({
27273
- code: "VALIDATION_ERROR",
27274
- operationIndex,
27275
- message: `Cannot ${op.operation} under retired collection shape "${local.shapePrefix}". Triple is read-only and retired for new collection writes.`
27276
- });
27277
- } else if (op.kind === "thing" && local && isBuiltinCollectionShape(local.shapePrefix)) {
27278
- const message = op.operation === "add" ? `Cannot add kind="thing" under built-in shape "${local.shapePrefix}". Use kind="collection" instead.` : `Cannot revise kind="thing" under built-in shape "${local.shapePrefix}". Use kind="collection" to revise a collection.`;
27279
- errors.push({
27280
- code: "VALIDATION_ERROR",
27281
- operationIndex,
27282
- message
27283
- });
27284
- }
27285
- }
27286
- return errors;
27287
- }
27288
- function contentNameGuard(op, operationIndex) {
27289
- if (op.kind !== "thing" || !op.name)
27290
- return [];
27291
- const local = splitLocalPath(op.name);
27292
- if (!local || !isContentShape(local.shapePrefix))
27293
- return [];
27294
- const bareName = local.bareName;
27295
- if (isSynthesizedContentName(bareName)) {
27296
- return [
27297
- {
27298
- code: "READ_ONLY_BUILTIN_CONTENT",
27299
- operationIndex,
27300
- message: `${local.shapePrefix}/${bareName} is synthesized; writes are not allowed`
27301
- }
27302
- ];
27303
- }
27304
- if (!isStoredContentName(bareName)) {
27305
- return [
27306
- {
27307
- code: "UNKNOWN_CONTENT_NAME",
27308
- operationIndex,
27309
- message: `${local.shapePrefix}/${bareName} is not a recognized well-known content name`
27310
- }
27311
- ];
27312
- }
27313
- return [];
27314
- }
27315
- function plusSignGuard(op, operationIndex) {
27316
- const errors = [];
27317
- const name = op.name;
27318
- if (op.newName?.includes("+")) {
27319
- errors.push({
27320
- code: "VALIDATION_ERROR",
27321
- operationIndex,
27322
- message: `Name "${op.newName}" contains reserved character "+". The "+" character is reserved for the historical collection auto-name namespace and is not allowed in user-supplied names.`
27323
- });
27324
- return errors;
27325
- }
27326
- if (name?.includes("+")) {
27327
- if (op.kind === "collection" && op.operation === "add") {
27328
- errors.push({
27329
- code: "VALIDATION_ERROR",
27330
- operationIndex,
27331
- message: `Name "${name}" contains reserved character "+". The "+" character is reserved for the historical collection auto-name namespace and is not allowed in user-supplied names.`
27332
- });
27333
- return errors;
27334
- }
27335
- if (op.kind === "collection")
27336
- return errors;
27337
- const local = splitLocalPath(name);
27338
- if (local && isReservedCollectionShape(local.shapePrefix))
27339
- return errors;
27340
- errors.push({
27341
- code: "VALIDATION_ERROR",
27342
- operationIndex,
27343
- message: `Name "${name}" contains reserved character "+". The "+" character is reserved for the historical collection auto-name namespace and is not allowed in user-supplied names.`
27344
- });
27345
- }
27346
- return errors;
27347
- }
27348
- function validateCollectionAbouts(op, operationIndex) {
27349
- const errors = [];
27350
- if (!op.about || typeof op.about === "string")
27351
- return errors;
27352
- errors.push({
27353
- code: "VALIDATION_ERROR",
27354
- operationIndex,
27355
- message: COLLECTION_ABOUT_REMOVED_MESSAGE
27356
- });
27357
- return errors;
27358
- }
27359
- function validateCollectionOps(op, operationIndex) {
27360
- const errors = [];
27361
- if (op.kind !== "collection")
27362
- return errors;
27363
- if (!collectionOps.includes(op.operation)) {
27364
- return errors;
27365
- }
27366
- if (op.operation === "add" && !op.name) {
27367
- errors.push({
27368
- code: "VALIDATION_ERROR",
27369
- operationIndex,
27370
- message: COLLECTION_CREATE_REQUIRES_NAME_MESSAGE
27371
- });
27372
- }
27373
- if (!op.type || !collectionTypes.includes(op.type)) {
27374
- errors.push({
27375
- code: "VALIDATION_ERROR",
27376
- operationIndex,
27377
- message: `Collection "type" is invalid. Use one of: ${canonicalCollectionTypes.join(", ")}. Got: "${op.type ?? ""}"`
27378
- });
27379
- }
27380
- if (!op.members || !Array.isArray(op.members)) {
27381
- errors.push({
27382
- code: "VALIDATION_ERROR",
27383
- operationIndex,
27384
- message: 'Collection requires a "members" array'
27385
- });
27386
- } else {
27387
- for (let i = 0;i < op.members.length; i++) {
27388
- if (typeof op.members[i] !== "string") {
27389
- errors.push({
27390
- code: "VALIDATION_ERROR",
27391
- operationIndex,
27392
- message: `Collection member at index ${i} must be a string`
27393
- });
27394
- }
27395
- }
27396
- }
27397
- if (op.type && op.members) {
27398
- const arityError = collectionArityError(op.type, op.members);
27399
- if (arityError) {
27400
- errors.push({
27401
- code: "VALIDATION_ERROR",
27402
- operationIndex,
27403
- message: arityError
27404
- });
27405
- }
27406
- }
27407
- return errors;
27408
- }
27409
- function collectionArityError(tag, members) {
27410
- switch (tag) {
27411
- case "arc":
27412
- return members.length !== 2 ? `Arc requires exactly 2 members, got ${members.length}` : null;
27413
- case "bond":
27414
- return members.length !== 2 ? `Bond requires exactly 2 members, got ${members.length}` : null;
27415
- case "pair":
27416
- return members.length !== 2 ? `Pair requires exactly 2 members, got ${members.length}` : null;
27417
- case "set":
27418
- case "list":
27419
- return members.length < 1 ? `${tag === "set" ? "Set" : "List"} requires at least 1 member, got 0` : null;
27420
- }
27421
- }
27422
- // ../../packages/rules/src/preflight.ts
27423
- function preflightOpDiagnostics2(op, operationIndex) {
27424
- return preflightOpDiagnostics(op, operationIndex);
27425
- }
27426
- function preflightCommitDiagnostics2(operations, options) {
27427
- return preflightCommitDiagnostics(operations, options);
27428
- }
27429
- // ../../packages/rules/src/read-limits.ts
27430
- var MAX_GET_MANY_WREFS = 500;
27431
- // ../../packages/rules/src/reserved-orgs.ts
27432
- var RESERVED_RAW_CONTENT_TOP_LEVEL_NAMES = [
27433
- "_app",
27434
- "_static",
27435
- "api",
27436
- "health",
27437
- "healthz",
27438
- "mcp",
27439
- "readyz",
27440
- "robots.txt",
27441
- "sse",
27442
- "trpc",
27443
- "version"
27444
- ];
27445
- var RESERVED_ORG_NAMES = [
27446
- ...RESERVED_RAW_CONTENT_TOP_LEVEL_NAMES,
27447
- "admin",
27448
- "billing",
27449
- "blog",
27450
- "docs",
27451
- "help",
27452
- "login",
27453
- "public",
27454
- "settings",
27455
- "signup",
27456
- "status",
27457
- "support",
27458
- "system",
27459
- "warmhub",
27460
- "www"
27461
- ];
27462
- var RESERVED_ORG_NAME_SET = new Set(RESERVED_ORG_NAMES);
27463
- // ../../packages/rules/src/shape-field-key.ts
27464
- function foldFieldName(name) {
27465
- const stripped = name.endsWith("?") ? name.slice(0, -1) : name;
27466
- return stripped.toLowerCase();
27467
- }
27468
- function foldFieldPath(path) {
27469
- return path.split(".").map((segment) => foldFieldName(segment)).join(".");
27470
- }
27471
27401
  // ../../packages/rules/src/shape-validation-runtime.ts
27472
27402
  function validatePersistedStringFieldLimits(value, typeDef, errors, path) {
27473
27403
  const normalized = normalizeOptionalTypeSpec(typeDef).spec;
@@ -28063,8 +27993,8 @@ function normalizeOptionalName(value) {
28063
27993
 
28064
27994
  // ../../packages/sdk-ts/src/operation-normalize.ts
28065
27995
  function toBackendStreamOperation(operation) {
28066
- if (operation.expectedVersion !== undefined && operation.operation !== "revise") {
28067
- throw new Error("expectedVersion is only valid on revise operations — set operation: 'revise'");
27996
+ if (operation.expectedVersion !== undefined && operation.operation !== "revise" && operation.operation !== "retract") {
27997
+ throw new Error("expectedVersion is only valid on revise or retract operations — set an explicit operation discriminator");
28068
27998
  }
28069
27999
  if (operation.operation === "retract") {
28070
28000
  return {
@@ -28072,6 +28002,7 @@ function toBackendStreamOperation(operation) {
28072
28002
  name: operation.name,
28073
28003
  ...operation.kind ? { kind: operation.kind } : {},
28074
28004
  ...operation.reason ? { reason: operation.reason } : {},
28005
+ ...operation.expectedVersion !== undefined ? { expectedVersion: operation.expectedVersion } : {},
28075
28006
  ...operation.leaseId ? { leaseId: operation.leaseId } : {}
28076
28007
  };
28077
28008
  }
@@ -28167,13 +28098,19 @@ class PartialStreamSubmissionError extends Error {
28167
28098
  code = "PARTIAL_STREAM_SUBMISSION";
28168
28099
  completedOperations;
28169
28100
  acknowledgedOperationCount;
28101
+ lastAcknowledgedRepoSeq;
28102
+ attemptedAppendOutcome;
28170
28103
  cause;
28171
28104
  constructor(input) {
28172
- super("Stream submission failed. A timed-out append may already have landed, so inspect repository state before sending any further operations.");
28105
+ super("Stream submission outcome is incomplete or unknown. Stop sending writes; once any outstanding append can no longer apply, obtain a later verified checkpoint and reconcile before replanning.");
28173
28106
  this.name = "WarmHubError";
28174
28107
  this.cause = input.cause;
28175
28108
  this.completedOperations = input.completedOperations;
28176
28109
  this.acknowledgedOperationCount = input.acknowledgedOperationCount ?? input.completedOperations.length;
28110
+ this.attemptedAppendOutcome = input.attemptedAppendOutcome;
28111
+ if (input.lastAcknowledgedRepoSeq !== undefined) {
28112
+ this.lastAcknowledgedRepoSeq = input.lastAcknowledgedRepoSeq;
28113
+ }
28177
28114
  }
28178
28115
  }
28179
28116
 
@@ -28330,6 +28267,9 @@ function isTransientStreamFailure(cause) {
28330
28267
  function isFetchNetworkTypeError(cause) {
28331
28268
  return cause instanceof TypeError && /fetch/i.test(cause.message);
28332
28269
  }
28270
+ function isReplaySafeOperation(operation) {
28271
+ return operation.operation !== "revise" || operation.expectedVersion !== undefined;
28272
+ }
28333
28273
  function computeBackoffDelayMs(attempt, policy) {
28334
28274
  const exponential = policy.baseDelayMs * 2 ** Math.max(0, attempt - 1);
28335
28275
  const jitter = Math.random() * policy.baseDelayMs;
@@ -28379,7 +28319,8 @@ async function submitOperationsViaStream(client, args) {
28379
28319
  for (const { operations: chunk, start: chunkStart } of chunkOperations(operations, chunkSize)) {
28380
28320
  let attempt = 1;
28381
28321
  let priorAttemptAmbiguous = false;
28382
- const chunkIsAtomic = chunk.length === 1;
28322
+ const chunkIsSingleOperation = chunk.length === 1;
28323
+ const chunkIsReplaySafe = chunk.every(isReplaySafeOperation);
28383
28324
  while (true) {
28384
28325
  try {
28385
28326
  const appendResult = await client.stream.append({
@@ -28407,7 +28348,7 @@ async function submitOperationsViaStream(client, args) {
28407
28348
  if (acknowledgedOperationCount === 0 && !priorAttemptAmbiguous && isDefiniteClientError(cause)) {
28408
28349
  throw cause;
28409
28350
  }
28410
- if (acknowledgedOperationCount === 0 && chunkIsAtomic && policy !== false && attempt < policy.maxAttempts && isTransientStreamFailure(cause)) {
28351
+ if (acknowledgedOperationCount === 0 && chunkIsSingleOperation && chunkIsReplaySafe && policy !== false && attempt < policy.maxAttempts && isTransientStreamFailure(cause)) {
28411
28352
  await sleep2(computeBackoffDelayMs(attempt, policy));
28412
28353
  attempt += 1;
28413
28354
  priorAttemptAmbiguous = true;
@@ -28422,7 +28363,9 @@ async function submitOperationsViaStream(client, args) {
28422
28363
  throw new PartialStreamSubmissionError({
28423
28364
  cause,
28424
28365
  completedOperations,
28425
- acknowledgedOperationCount
28366
+ acknowledgedOperationCount,
28367
+ lastAcknowledgedRepoSeq: repoSeq,
28368
+ attemptedAppendOutcome: !priorAttemptAmbiguous && isDefiniteClientError(cause) ? "not_applied" : "unknown"
28426
28369
  });
28427
28370
  }
28428
28371
  }
@@ -28436,7 +28379,9 @@ async function submitOperationsViaStream(client, args) {
28436
28379
  throw new PartialStreamSubmissionError({
28437
28380
  cause: failure,
28438
28381
  completedOperations: completedOperationsFrom(result),
28439
- acknowledgedOperationCount
28382
+ acknowledgedOperationCount,
28383
+ lastAcknowledgedRepoSeq: repoSeq,
28384
+ attemptedAppendOutcome: "unknown"
28440
28385
  });
28441
28386
  }
28442
28387
  throw failure;
@@ -28550,7 +28495,7 @@ function dedupeDeprecationsByShape(results) {
28550
28495
  // ../../packages/sdk-ts/package.json
28551
28496
  var package_default = {
28552
28497
  name: "@warmhub/sdk-ts",
28553
- version: "0.82.3",
28498
+ version: "0.84.0",
28554
28499
  private: false,
28555
28500
  type: "module",
28556
28501
  description: "The TypeScript SDK for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",
@@ -28705,40 +28650,64 @@ function assertPaginatedCursorAdvanced(cursor, nextCursor, seenCursors) {
28705
28650
  throw new WarmHubError("VALIDATION_ERROR", "Paginated result cursor did not advance");
28706
28651
  }
28707
28652
  }
28708
- async function* paginate(fetchPage, extractItems, initialCursor) {
28653
+ function assertIncrementalLowerBound(sinceRepoSeq) {
28654
+ if (sinceRepoSeq === undefined) {
28655
+ throw new WarmHubError("VALIDATION_ERROR", "sinceRepoSeq is required for an incremental read");
28656
+ }
28657
+ }
28658
+ async function* paginatePages(fetchPage, initialCursor) {
28709
28659
  let cursor = initialCursor;
28710
28660
  const seenCursors = new Set;
28711
28661
  for (;; ) {
28712
28662
  if (cursor)
28713
28663
  seenCursors.add(cursor);
28714
28664
  const page = await fetchPage(cursor);
28715
- for (const item of extractItems(page)) {
28716
- yield item;
28717
- }
28665
+ yield page;
28718
28666
  if (!page.nextCursor)
28719
28667
  return;
28720
28668
  assertPaginatedCursorAdvanced(cursor, page.nextCursor, seenCursors);
28721
28669
  cursor = page.nextCursor;
28722
28670
  }
28723
28671
  }
28672
+ async function* paginate(fetchPage, extractItems, initialCursor) {
28673
+ for await (const page of paginatePages(fetchPage, initialCursor)) {
28674
+ yield* extractItems(page);
28675
+ }
28676
+ }
28724
28677
  async function collectPaginatedPages(fetchPage, extractItems, max, initialCursor) {
28725
28678
  if (max !== undefined && (!Number.isFinite(max) || Math.trunc(max) !== max || max < 0)) {
28726
28679
  throw new WarmHubError("VALIDATION_ERROR", "max must be a nonnegative integer");
28727
28680
  }
28728
28681
  const items = [];
28729
- let cursor = initialCursor;
28682
+ for await (const page of paginatePages(fetchPage, initialCursor)) {
28683
+ const pageItems = extractItems(page);
28684
+ if (max !== undefined && items.length + pageItems.length > max) {
28685
+ throw new WarmHubError("VALIDATION_ERROR", `Paginated result exceeded max=${max}`);
28686
+ }
28687
+ items.push(...pageItems);
28688
+ }
28689
+ return items;
28690
+ }
28691
+ function assertRepoExploreBrowseMode(opts) {
28692
+ if (opts?.slugs !== undefined) {
28693
+ throw new WarmHubError("VALIDATION_ERROR", "repo.explore pagination helpers do not support slugs mode");
28694
+ }
28695
+ }
28696
+ async function collectIncrementalPages(fetchPage) {
28697
+ const items = [];
28698
+ let cursor;
28730
28699
  const seenCursors = new Set;
28731
28700
  for (;; ) {
28732
28701
  if (cursor)
28733
28702
  seenCursors.add(cursor);
28734
28703
  const page = await fetchPage(cursor);
28735
- const pageItems = extractItems(page);
28736
- if (max !== undefined && items.length + pageItems.length > max) {
28737
- throw new WarmHubError("VALIDATION_ERROR", `Paginated result exceeded max=${max}`);
28704
+ items.push(...page.items);
28705
+ if (!page.nextCursor) {
28706
+ if (page.repoSeq === undefined) {
28707
+ throw new WarmHubError("VALIDATION_ERROR", "Incremental read completed without repoSeq");
28708
+ }
28709
+ return { items, repoSeq: page.repoSeq };
28738
28710
  }
28739
- items.push(...pageItems);
28740
- if (!page.nextCursor)
28741
- return items;
28742
28711
  assertPaginatedCursorAdvanced(cursor, page.nextCursor, seenCursors);
28743
28712
  cursor = page.nextCursor;
28744
28713
  }
@@ -28755,6 +28724,9 @@ function chunkArray(items, chunkSize) {
28755
28724
  }
28756
28725
  return chunks;
28757
28726
  }
28727
+ function narrowKind(kind) {
28728
+ return kind !== undefined && COMMIT_OPERATION_KINDS.includes(kind) ? kind : undefined;
28729
+ }
28758
28730
  function toSubscriptionRef(args) {
28759
28731
  const [first, repoName, name] = args;
28760
28732
  return typeof first === "string" ? { orgName: first, repoName, name } : first;
@@ -29141,6 +29113,13 @@ class WarmHubClient {
29141
29113
  throw toWarmHubError(error);
29142
29114
  }
29143
29115
  },
29116
+ listIter: (orgName, repoName, opts) => {
29117
+ return paginate((cursor) => this.component.list(orgName, repoName, { ...opts, cursor }), (page) => page.items, opts?.cursor);
29118
+ },
29119
+ listAll: async (orgName, repoName, opts) => {
29120
+ const { max, ...pageOpts } = opts ?? {};
29121
+ return await collectPaginatedPages((cursor) => this.component.list(orgName, repoName, { ...pageOpts, cursor }), (page) => page.items, max, pageOpts.cursor);
29122
+ },
29144
29123
  search: async (query, opts) => {
29145
29124
  try {
29146
29125
  return await this.trpc.component.search.query({
@@ -29152,6 +29131,13 @@ class WarmHubClient {
29152
29131
  throw toWarmHubError(error);
29153
29132
  }
29154
29133
  },
29134
+ searchIter: (query, opts) => {
29135
+ return paginate((cursor) => this.component.search(query, { ...opts, cursor }), (page) => page.items, opts?.cursor);
29136
+ },
29137
+ searchAll: async (query, opts) => {
29138
+ const { max, ...pageOpts } = opts ?? {};
29139
+ return await collectPaginatedPages((cursor) => this.component.search(query, { ...pageOpts, cursor }), (page) => page.items, max, pageOpts.cursor);
29140
+ },
29155
29141
  get: async (orgName, repoName, componentRef) => {
29156
29142
  try {
29157
29143
  return await this.trpc.component.get.query({
@@ -29531,6 +29517,13 @@ class WarmHubClient {
29531
29517
  throw toWarmHubError(error);
29532
29518
  }
29533
29519
  },
29520
+ listIter: (orgName, opts) => {
29521
+ return paginate((cursor) => this.repo.list(orgName, { ...opts, cursor }), (page) => page.items, opts?.cursor);
29522
+ },
29523
+ listAll: async (orgName, opts) => {
29524
+ const { max, ...pageOpts } = opts ?? {};
29525
+ return await collectPaginatedPages((cursor) => this.repo.list(orgName, { ...pageOpts, cursor }), (page) => page.items, max, pageOpts.cursor);
29526
+ },
29534
29527
  search: async (query, opts) => {
29535
29528
  try {
29536
29529
  return await this.trpc.repo.search.query({
@@ -29542,6 +29535,13 @@ class WarmHubClient {
29542
29535
  throw toWarmHubError(error);
29543
29536
  }
29544
29537
  },
29538
+ searchIter: (query, opts) => {
29539
+ return paginate((cursor) => this.repo.search(query, { ...opts, cursor }), (page) => page.items, opts?.cursor);
29540
+ },
29541
+ searchAll: async (query, opts) => {
29542
+ const { max, ...pageOpts } = opts ?? {};
29543
+ return await collectPaginatedPages((cursor) => this.repo.search(query, { ...pageOpts, cursor }), (page) => page.items, max, pageOpts.cursor);
29544
+ },
29545
29545
  create: async (orgName, repoName, description, visibility, displayName) => {
29546
29546
  try {
29547
29547
  return await this.trpc.repo.create.mutate({
@@ -29651,6 +29651,13 @@ class WarmHubClient {
29651
29651
  throw toWarmHubError(error);
29652
29652
  }
29653
29653
  },
29654
+ listPageIter: (orgName, opts) => {
29655
+ return paginate((cursor) => this.repo.listPage(orgName, { ...opts, cursor }), (page) => page.items, opts?.cursor);
29656
+ },
29657
+ listPageAll: async (orgName, opts) => {
29658
+ const { max, ...pageOpts } = opts ?? {};
29659
+ return await collectPaginatedPages((cursor) => this.repo.listPage(orgName, { ...pageOpts, cursor }), (page) => page.items, max, pageOpts.cursor);
29660
+ },
29654
29661
  listForCaller: async (opts) => {
29655
29662
  try {
29656
29663
  return await this.trpc.repo.listForCaller.query({
@@ -29678,6 +29685,15 @@ class WarmHubClient {
29678
29685
  throw toWarmHubError(error);
29679
29686
  }
29680
29687
  },
29688
+ exploreIter: (opts) => {
29689
+ assertRepoExploreBrowseMode(opts);
29690
+ return paginate((cursor) => this.repo.explore({ ...opts, cursor }), (page) => page.items, opts?.cursor);
29691
+ },
29692
+ exploreAll: async (opts) => {
29693
+ assertRepoExploreBrowseMode(opts);
29694
+ const { max, ...pageOpts } = opts ?? {};
29695
+ return await collectPaginatedPages((cursor) => this.repo.explore({ ...pageOpts, cursor }), (page) => page.items, max, pageOpts.cursor);
29696
+ },
29681
29697
  getReadme: async (orgName, repoName) => {
29682
29698
  try {
29683
29699
  return await this.trpc.repo.getReadme.query({
@@ -29866,6 +29882,13 @@ class WarmHubClient {
29866
29882
  } catch (error) {
29867
29883
  throw toWarmHubError(error);
29868
29884
  }
29885
+ },
29886
+ historyIter: (orgName, repoName, name, opts = {}) => {
29887
+ return paginate((cursor) => this.shape.history(orgName, repoName, name, { ...opts, cursor }), (page) => page.versions, opts.cursor);
29888
+ },
29889
+ historyAll: async (orgName, repoName, name, opts) => {
29890
+ const { max, ...pageOpts } = opts ?? {};
29891
+ return await collectPaginatedPages((cursor) => this.shape.history(orgName, repoName, name, { ...pageOpts, cursor }), (page) => page.versions, max, pageOpts.cursor);
29869
29892
  }
29870
29893
  };
29871
29894
  subscription = {
@@ -30024,11 +30047,25 @@ class WarmHubClient {
30024
30047
  throw toWarmHubError(error);
30025
30048
  }
30026
30049
  },
30050
+ liveFeedIter: (orgName, repoName, subscriptionName, opts) => {
30051
+ return paginate((cursor) => this.action.liveFeed(orgName, repoName, subscriptionName, {
30052
+ ...opts,
30053
+ cursor
30054
+ }), (page) => page.items, opts?.cursor);
30055
+ },
30056
+ liveFeedAll: async (orgName, repoName, subscriptionName, opts) => {
30057
+ const { max, ...pageOpts } = opts ?? {};
30058
+ return await collectPaginatedPages((cursor) => this.action.liveFeed(orgName, repoName, subscriptionName, {
30059
+ ...pageOpts,
30060
+ cursor
30061
+ }), (page) => page.items, max, pageOpts.cursor);
30062
+ },
30027
30063
  listRuns: async (orgName, repoName, opts) => {
30028
30064
  try {
30029
30065
  return await this.trpc.action.listRuns.query({
30030
30066
  orgName,
30031
30067
  repoName,
30068
+ runId: opts?.runId,
30032
30069
  subscriptionName: opts?.subscriptionName,
30033
30070
  status: opts?.status,
30034
30071
  outcome: opts?.outcome,
@@ -30040,6 +30077,21 @@ class WarmHubClient {
30040
30077
  throw toWarmHubError(error);
30041
30078
  }
30042
30079
  },
30080
+ listRunsIter: (orgName, repoName, opts) => {
30081
+ return paginate((cursor) => this.action.listRuns(orgName, repoName, {
30082
+ ...opts,
30083
+ since: cursor === undefined ? opts?.since : undefined,
30084
+ cursor
30085
+ }), (page) => page.items, opts?.cursor);
30086
+ },
30087
+ listRunsAll: async (orgName, repoName, opts) => {
30088
+ const { max, ...pageOpts } = opts ?? {};
30089
+ return await collectPaginatedPages((cursor) => this.action.listRuns(orgName, repoName, {
30090
+ ...pageOpts,
30091
+ since: cursor === undefined ? pageOpts.since : undefined,
30092
+ cursor
30093
+ }), (page) => page.items, max, pageOpts.cursor);
30094
+ },
30043
30095
  latestRuns: async (orgName, repoName) => {
30044
30096
  try {
30045
30097
  return await this.trpc.action.latestRuns.query({ orgName, repoName });
@@ -30224,12 +30276,11 @@ class WarmHubClient {
30224
30276
  thing = {
30225
30277
  head: async (orgName, repoName, opts) => {
30226
30278
  try {
30227
- const kind = opts?.kind === "shape" || opts?.kind === "thing" || opts?.kind === "assertion" || opts?.kind === "collection" ? opts.kind : undefined;
30228
30279
  return await this.trpc.thing.head.query({
30229
30280
  orgName,
30230
30281
  repoName,
30231
30282
  shape: opts?.shape,
30232
- kind,
30283
+ kind: narrowKind(opts?.kind),
30233
30284
  match: opts?.match,
30234
30285
  dataMode: opts?.dataMode,
30235
30286
  includeRetracted: opts?.includeRetracted,
@@ -30238,7 +30289,8 @@ class WarmHubClient {
30238
30289
  componentRef: opts?.componentRef,
30239
30290
  excludeComponents: opts?.excludeComponents,
30240
30291
  excludeInfraShapes: opts?.excludeInfraShapes,
30241
- where: opts?.where
30292
+ where: opts?.where,
30293
+ sinceRepoSeq: opts?.sinceRepoSeq
30242
30294
  });
30243
30295
  } catch (error) {
30244
30296
  throw toWarmHubError(error);
@@ -30251,6 +30303,14 @@ class WarmHubClient {
30251
30303
  const { max, ...pageOpts } = opts ?? {};
30252
30304
  return await collectPaginatedPages((cursor) => this.thing.head(orgName, repoName, { ...pageOpts, cursor }), (page) => page.items, max, pageOpts.cursor);
30253
30305
  },
30306
+ headChanges: async (orgName, repoName, opts) => {
30307
+ assertIncrementalLowerBound(opts?.sinceRepoSeq);
30308
+ return await collectIncrementalPages((cursor) => this.thing.head(orgName, repoName, {
30309
+ ...opts,
30310
+ includeRetracted: true,
30311
+ cursor
30312
+ }));
30313
+ },
30254
30314
  get: async (orgName, repoName, wref, version, opts) => {
30255
30315
  try {
30256
30316
  return await this.trpc.thing.get.query({
@@ -30394,6 +30454,13 @@ class WarmHubClient {
30394
30454
  throw toWarmHubError(error);
30395
30455
  }
30396
30456
  },
30457
+ historyIter: (orgName, repoName, opts) => {
30458
+ return paginate((cursor) => this.thing.history(orgName, repoName, { ...opts, cursor }), (page) => page.versions, opts.cursor);
30459
+ },
30460
+ historyAll: async (orgName, repoName, opts) => {
30461
+ const { max, ...pageOpts } = opts;
30462
+ return await collectPaginatedPages((cursor) => this.thing.history(orgName, repoName, { ...pageOpts, cursor }), (page) => page.versions, max, pageOpts.cursor);
30463
+ },
30397
30464
  rename: async (orgName, repoName, shapeName, oldName, newName) => {
30398
30465
  try {
30399
30466
  return await this.trpc.thing.rename.mutate({
@@ -30447,13 +30514,12 @@ class WarmHubClient {
30447
30514
  },
30448
30515
  query: async (orgName, repoName, opts) => {
30449
30516
  try {
30450
- const kind = opts?.kind === "shape" || opts?.kind === "thing" || opts?.kind === "assertion" || opts?.kind === "collection" ? opts.kind : undefined;
30451
30517
  return await this.trpc.thing.query.query({
30452
30518
  orgName,
30453
30519
  repoName,
30454
30520
  shape: opts?.shape,
30455
30521
  about: opts?.about,
30456
- kind,
30522
+ kind: narrowKind(opts?.kind),
30457
30523
  match: opts?.match,
30458
30524
  includeRetracted: opts?.includeRetracted,
30459
30525
  resolveCollections: opts?.resolveCollections,
@@ -30463,7 +30529,8 @@ class WarmHubClient {
30463
30529
  componentRef: opts?.componentRef,
30464
30530
  excludeComponents: opts?.excludeComponents,
30465
30531
  excludeInfraShapes: opts?.excludeInfraShapes,
30466
- where: opts?.where
30532
+ where: opts?.where,
30533
+ sinceRepoSeq: opts?.sinceRepoSeq
30467
30534
  });
30468
30535
  } catch (error) {
30469
30536
  throw toWarmHubError(error);
@@ -30476,16 +30543,23 @@ class WarmHubClient {
30476
30543
  const { max, ...pageOpts } = opts ?? {};
30477
30544
  return await collectPaginatedPages((cursor) => this.thing.query(orgName, repoName, { ...pageOpts, cursor }), (page) => page.items, max, pageOpts.cursor);
30478
30545
  },
30546
+ queryChanges: async (orgName, repoName, opts) => {
30547
+ assertIncrementalLowerBound(opts?.sinceRepoSeq);
30548
+ return await collectIncrementalPages((cursor) => this.thing.query(orgName, repoName, {
30549
+ ...opts,
30550
+ includeRetracted: true,
30551
+ cursor
30552
+ }));
30553
+ },
30479
30554
  search: async (orgName, repoName, query, opts) => {
30480
30555
  try {
30481
- const kind = opts?.kind === "shape" || opts?.kind === "thing" || opts?.kind === "assertion" || opts?.kind === "collection" ? opts.kind : undefined;
30482
30556
  return await this.trpc.thing.search.query({
30483
30557
  orgName,
30484
30558
  repoName,
30485
30559
  query,
30486
30560
  shape: opts?.shape,
30487
30561
  about: opts?.about,
30488
- kind,
30562
+ kind: narrowKind(opts?.kind),
30489
30563
  match: opts?.match,
30490
30564
  includeRetracted: opts?.includeRetracted,
30491
30565
  resolveCollections: opts?.resolveCollections,
@@ -30501,22 +30575,32 @@ class WarmHubClient {
30501
30575
  throw toWarmHubError(error);
30502
30576
  }
30503
30577
  },
30578
+ searchIter: (orgName, repoName, query, opts) => {
30579
+ return paginate((cursor) => this.thing.search(orgName, repoName, query, { ...opts, cursor }), (page) => page.items, opts?.cursor);
30580
+ },
30581
+ searchAll: async (orgName, repoName, query, opts) => {
30582
+ const { max, ...pageOpts } = opts ?? {};
30583
+ return await collectPaginatedPages((cursor) => this.thing.search(orgName, repoName, query, {
30584
+ ...pageOpts,
30585
+ cursor
30586
+ }), (page) => page.items, max, pageOpts.cursor);
30587
+ },
30504
30588
  count: async (orgName, repoName, opts) => {
30505
30589
  try {
30506
- const kind = opts?.kind === "shape" || opts?.kind === "thing" || opts?.kind === "assertion" || opts?.kind === "collection" ? opts.kind : undefined;
30507
30590
  return await this.trpc.thing.count.query({
30508
30591
  orgName,
30509
30592
  repoName,
30510
30593
  shape: opts?.shape,
30511
30594
  about: opts?.about,
30512
- kind,
30595
+ kind: narrowKind(opts?.kind),
30513
30596
  match: opts?.match,
30514
30597
  includeRetracted: opts?.includeRetracted,
30515
30598
  resolveCollections: opts?.resolveCollections,
30516
30599
  componentRef: opts?.componentRef,
30517
30600
  excludeComponents: opts?.excludeComponents,
30518
30601
  excludeInfraShapes: opts?.excludeInfraShapes,
30519
- where: opts?.where
30602
+ where: opts?.where,
30603
+ sinceRepoSeq: opts?.sinceRepoSeq
30520
30604
  });
30521
30605
  } catch (error) {
30522
30606
  throw toWarmHubError(error);
@@ -31044,6 +31128,7 @@ var _orgActivityNeedsNoFilter = ["org.member_added", "org.repo_created", "org.re
31044
31128
  }));
31045
31129
 
31046
31130
  // ../../packages/warmhub-cli/src/errors-classification.ts
31131
+ var INCREMENTAL_READ_UNAVAILABLE_GUIDANCE = "Retry the same incremental request after incremental reads are enabled, or simplify the query to a supported form. Do not fall back to a full read.";
31047
31132
  function unauthenticatedHint(message) {
31048
31133
  if (message.includes("Authentication required")) {
31049
31134
  return "Not logged in. Run `wh auth login` to authenticate.";
@@ -31070,6 +31155,8 @@ function classifyConflict(message, code) {
31070
31155
  return "pending-delete";
31071
31156
  case "LEASE_UNAVAILABLE":
31072
31157
  return "lease-held";
31158
+ case "INCREMENTAL_READ_UNAVAILABLE":
31159
+ return "incremental-read-unavailable";
31073
31160
  default:
31074
31161
  break;
31075
31162
  }
@@ -31103,6 +31190,8 @@ function conflictHint(message, code) {
31103
31190
  return "Repo is being deleted. Wait for the delete to finish or cancel it before retrying.";
31104
31191
  case "lease-held":
31105
31192
  return "This thing is leased by another holder. Back off until the lease expires (see leaseExpiresAt / retryAfter), then retry.";
31193
+ case "incremental-read-unavailable":
31194
+ return INCREMENTAL_READ_UNAVAILABLE_GUIDANCE;
31106
31195
  case "other":
31107
31196
  return "Resolve the conflicting existing state and try again.";
31108
31197
  }
@@ -31130,7 +31219,8 @@ var CONFLICT_SHAPED_CODES = new Set([
31130
31219
  "HAS_INBOUND_REFS",
31131
31220
  "REPO_PENDING_DELETE",
31132
31221
  "ALREADY_RETRACTED",
31133
- "LEASE_UNAVAILABLE"
31222
+ "LEASE_UNAVAILABLE",
31223
+ "INCREMENTAL_READ_UNAVAILABLE"
31134
31224
  ]);
31135
31225
 
31136
31226
  // ../../packages/warmhub-cli/src/errors-types.ts
@@ -31140,20 +31230,101 @@ class CliError extends Error {
31140
31230
  cause;
31141
31231
  hint;
31142
31232
  backendCode;
31233
+ recovery;
31143
31234
  context;
31144
- constructor(code, kind, message, cause, hint, context, backendCode) {
31235
+ constructor(code, kind, message, cause, hint, context, backendCode, recovery) {
31145
31236
  super(message);
31146
31237
  this.code = code;
31147
31238
  this.kind = kind;
31148
31239
  this.cause = cause;
31149
31240
  this.hint = hint;
31150
31241
  this.backendCode = backendCode;
31242
+ this.recovery = recovery;
31151
31243
  this.context = context;
31152
31244
  }
31153
31245
  get errorCode() {
31154
31246
  return this.backendCode;
31155
31247
  }
31156
31248
  }
31249
+
31250
+ // ../../packages/warmhub-cli/src/stream-recovery.ts
31251
+ function failureCode(error) {
31252
+ if (typeof error !== "object" || error === null)
31253
+ return;
31254
+ const typed = error;
31255
+ if (typeof typed.errorCode === "string")
31256
+ return typed.errorCode;
31257
+ if (typeof typed.backendCode === "string")
31258
+ return typed.backendCode;
31259
+ if (typeof typed.data?.warmhub?.code === "string") {
31260
+ return typed.data.warmhub.code;
31261
+ }
31262
+ if (typeof typed.data?.code === "string")
31263
+ return typed.data.code;
31264
+ return failureCode(typed.cause);
31265
+ }
31266
+ function failureStatus(error) {
31267
+ if (typeof error !== "object" || error === null)
31268
+ return;
31269
+ const typed = error;
31270
+ if (typeof typed.status === "number")
31271
+ return typed.status;
31272
+ if (typeof typed.data?.warmhub?.status === "number") {
31273
+ return typed.data.warmhub.status;
31274
+ }
31275
+ if (typeof typed.data?.status === "number")
31276
+ return typed.data.status;
31277
+ if (typeof typed.data?.httpStatus === "number") {
31278
+ return typed.data.httpStatus;
31279
+ }
31280
+ return failureStatus(typed.cause);
31281
+ }
31282
+ function streamAppendWasExplicitlyRejected(cause) {
31283
+ const normalized = toWarmHubError(cause);
31284
+ const code = failureCode(cause) ?? normalized.errorCode ?? normalized.code;
31285
+ if (code === "COMMIT_OUTCOME_UNKNOWN")
31286
+ return false;
31287
+ const status = failureStatus(cause) ?? normalized.status;
31288
+ return status !== undefined && status >= 400 && status < 500 && status !== 408;
31289
+ }
31290
+ function streamFailureCode(cause) {
31291
+ const normalized = toWarmHubError(cause);
31292
+ const wireCode = failureCode(cause) ?? normalized.errorCode;
31293
+ if (wireCode !== undefined)
31294
+ return wireCode;
31295
+ return normalized.status !== undefined && normalized.code !== "BACKEND" ? normalized.code : undefined;
31296
+ }
31297
+ function buildStreamRecoveryReceipt(args) {
31298
+ const {
31299
+ acknowledgedOperationCount,
31300
+ lastAcknowledgedRepoSeq,
31301
+ cause,
31302
+ attemptedAppendOutcome
31303
+ } = args;
31304
+ const sequenceReceipt = lastAcknowledgedRepoSeq === undefined ? "" : ` Last acknowledged repo sequence: ${lastAcknowledgedRepoSeq}.`;
31305
+ const errorCode = streamFailureCode(cause);
31306
+ const backendSuffix = errorCode ? ` (backend: ${errorCode})` : "";
31307
+ const recovery = {
31308
+ acknowledgedOperationCount,
31309
+ ...lastAcknowledgedRepoSeq === undefined ? {} : { lastAcknowledgedRepoSeq },
31310
+ attemptedAppendOutcome
31311
+ };
31312
+ if (attemptedAppendOutcome === "unknown") {
31313
+ return {
31314
+ message: `Stream append outcome is unknown after ${acknowledgedOperationCount} acknowledged operation(s).${sequenceReceipt}${backendSuffix}`,
31315
+ hint: "Stop submitting writes. The attempted append may have landed after the last acknowledgement; once it can no longer apply, obtain and verify a later checkpoint, then reconcile the attempted append and unsent work.",
31316
+ errorCode,
31317
+ recovery
31318
+ };
31319
+ }
31320
+ const causeMessage = cause instanceof Error ? cause.message : String(cause);
31321
+ return {
31322
+ message: `${causeMessage} (${acknowledgedOperationCount} earlier operation(s) already acknowledged before this failure).${sequenceReceipt}${backendSuffix}`,
31323
+ hint: "The failed input or rejected append was not applied. Keep the acknowledged prefix receipt, then correct or replan the rejected and unsent operations.",
31324
+ errorCode,
31325
+ recovery
31326
+ };
31327
+ }
31157
31328
  // ../../packages/warmhub-cli/src/errors-logging.ts
31158
31329
  function shouldLogToErrorTier(err) {
31159
31330
  switch (err.kind) {
@@ -31274,6 +31445,11 @@ function generateSuggestions(code, message, context, errorCode) {
31274
31445
  action: "Another holder leased this thing. Back off until the lease expires (leaseExpiresAt / retryAfter), then retry"
31275
31446
  });
31276
31447
  break;
31448
+ case "incremental-read-unavailable":
31449
+ suggestions.push({
31450
+ action: INCREMENTAL_READ_UNAVAILABLE_GUIDANCE
31451
+ });
31452
+ break;
31277
31453
  case "other":
31278
31454
  suggestions.push({
31279
31455
  action: "Resolve the conflicting existing state and retry"
@@ -31373,7 +31549,8 @@ function printCliError(err, errWriter, opts = {}) {
31373
31549
  message: err.message,
31374
31550
  ...err.hint ? { hint: err.hint } : {},
31375
31551
  ...suggestions.length > 0 ? { suggestions } : {},
31376
- ...err.context ? { context: err.context } : {}
31552
+ ...err.context ? { context: err.context } : {},
31553
+ ...err.recovery ? { recovery: err.recovery } : {}
31377
31554
  }
31378
31555
  };
31379
31556
  errWriter(JSON.stringify(envelope));
@@ -31413,8 +31590,13 @@ function toCliError(err) {
31413
31590
  return cliErrorFromAllFailed(err.operations);
31414
31591
  }
31415
31592
  if (err instanceof PartialStreamSubmissionError) {
31416
- const hint = err.cause instanceof Error ? err.cause.message : err.cause === undefined ? undefined : String(err.cause);
31417
- return new CliError(4 /* Backend */, "BACKEND", err.message, err, hint);
31593
+ const receipt = buildStreamRecoveryReceipt({
31594
+ acknowledgedOperationCount: err.acknowledgedOperationCount,
31595
+ lastAcknowledgedRepoSeq: err.lastAcknowledgedRepoSeq,
31596
+ cause: err.cause,
31597
+ attemptedAppendOutcome: err.attemptedAppendOutcome
31598
+ });
31599
+ return new CliError(4 /* Backend */, "BACKEND", receipt.message, err, receipt.hint, undefined, receipt.errorCode, receipt.recovery);
31418
31600
  }
31419
31601
  if (err instanceof WarmHubError) {
31420
31602
  if (err.code === "CURSOR_EPOCH_INVALID" || err.errorCode === "CURSOR_EPOCH_INVALID") {
@@ -34886,6 +35068,7 @@ function pageEnvelope(items, opts) {
34886
35068
  const nextCursor = opts.nextCursor ?? null;
34887
35069
  return {
34888
35070
  items,
35071
+ ...opts.repoSeq === undefined ? {} : { repoSeq: opts.repoSeq },
34889
35072
  page: {
34890
35073
  limit: opts.limit,
34891
35074
  count: items.length,
@@ -35038,6 +35221,9 @@ var reviseFlags = {
35038
35221
  };
35039
35222
  var retractFlags = {
35040
35223
  reason: flag.string({ description: "Reason for retraction (<=500 chars)" }),
35224
+ "expected-version": flag.number({
35225
+ description: "only retract if the target is still at this version (optimistic concurrency)"
35226
+ }),
35041
35227
  message: flag.string({ short: "m", description: "Commit message" }),
35042
35228
  committer: flag.string({
35043
35229
  description: "Committer wref (shape or shaped thing; e.g. Agent/bot-1)"
@@ -35072,15 +35258,17 @@ var handleRevise = async (ctx, { flags, args }) => {
35072
35258
  var handleRetract = async (ctx, { flags, args }) => {
35073
35259
  const name = args[0];
35074
35260
  if (!name) {
35075
- usageError("Usage: wh assertion retract <wref> -m <message>", 'wh assertion retract Belief/cave-safe -m "withdraw claim"');
35261
+ usageError("Usage: wh assertion retract <wref> -m <message> [--expected-version <n>]", 'wh assertion retract Belief/cave-safe -m "withdraw claim"');
35076
35262
  }
35077
35263
  const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
35264
+ const expectedVersion = parsePositiveIntFlag(flags["expected-version"], "--expected-version", "wh assertion retract Belief/cave-safe --expected-version 3");
35078
35265
  const result = await ctx.client.commit.apply(org, repo, flags.message ?? `retract ${name}`, [
35079
35266
  {
35080
35267
  operation: "retract",
35081
35268
  kind: "assertion",
35082
35269
  name,
35083
- ...flags.reason ? { reason: flags.reason } : {}
35270
+ ...flags.reason ? { reason: flags.reason } : {},
35271
+ ...expectedVersion !== undefined ? { expectedVersion } : {}
35084
35272
  }
35085
35273
  ], {
35086
35274
  committer: flags.committer
@@ -35155,6 +35343,47 @@ function parseCollectionRoleFlag(value, example) {
35155
35343
  return value;
35156
35344
  usageError("Invalid --role. Expected from, to, or ends.", example);
35157
35345
  }
35346
+ function parseSinceRepoSeqFlag(value) {
35347
+ if (value === undefined)
35348
+ return;
35349
+ if (Number.isSafeInteger(value) && value >= -1)
35350
+ return value;
35351
+ usageError("--since-repo-seq must be -1 or a nonnegative safe integer.", "wh thing list --since-repo-seq -1 --all --format json");
35352
+ }
35353
+ function requireIncrementalCheckpoint(repoSeq, required) {
35354
+ if (required && repoSeq === undefined) {
35355
+ throw new CliError(4 /* Backend */, "BACKEND", "Incremental read completed without repoSeq", undefined, "Retry the read. If the checkpoint remains absent, report a backend incremental-read contract violation.");
35356
+ }
35357
+ }
35358
+ async function collectThingPages(args) {
35359
+ const items = [];
35360
+ let cursor = args.initialCursor;
35361
+ const seenCursors = new Set(cursor ? [cursor] : []);
35362
+ while (true) {
35363
+ const page = await args.fetchPage(cursor);
35364
+ const pageItems = page.items ?? [];
35365
+ if (args.onPage) {
35366
+ if (!await args.onPage(pageItems)) {
35367
+ return { items, nextCursor: undefined };
35368
+ }
35369
+ } else {
35370
+ items.push(...pageItems);
35371
+ }
35372
+ if (!page.nextCursor) {
35373
+ requireIncrementalCheckpoint(page.repoSeq, args.requireRepoSeq);
35374
+ return {
35375
+ items,
35376
+ nextCursor: undefined,
35377
+ ...page.repoSeq === undefined ? {} : { repoSeq: page.repoSeq }
35378
+ };
35379
+ }
35380
+ if (seenCursors.has(page.nextCursor)) {
35381
+ throw new CliError(4 /* Backend */, "BACKEND", `${args.title} pagination cursor repeated; aborting --all to avoid an infinite loop.`, undefined, "Retry without --all to fetch one page and inspect page.nextCursor. If cursors repeat, report this as a backend pagination issue.");
35382
+ }
35383
+ seenCursors.add(page.nextCursor);
35384
+ cursor = page.nextCursor;
35385
+ }
35386
+ }
35158
35387
  async function handleCount(ctx, org, repo, opts) {
35159
35388
  const result = await ctx.client.thing.count(org, repo, opts);
35160
35389
  writeOutput(ctx, result, () => ctx.out(`${result.count}`));
@@ -36110,6 +36339,9 @@ var headFlags = {
36110
36339
  description: "Max items per page (default: 50, max: 500)"
36111
36340
  }),
36112
36341
  cursor: flag.string({ description: "Opaque pagination cursor" }),
36342
+ "since-repo-seq": flag.number({
36343
+ description: "Return identities changed after this repository sequence"
36344
+ }),
36113
36345
  all: flag.boolean({ description: "Fetch all pages" }),
36114
36346
  match: flag.string({ description: "Filter by wref glob pattern" }),
36115
36347
  count: flag.boolean({ description: "Return count of matching items" }),
@@ -36133,15 +36365,19 @@ var handleHead = async (ctx, { flags, args }) => {
36133
36365
  const kind = validateKind(flags.kind);
36134
36366
  const limit = flags.limit;
36135
36367
  const cursor = flags.cursor;
36368
+ const sinceRepoSeq = parseSinceRepoSeqFlag(flags["since-repo-seq"]);
36136
36369
  const all = flags.all;
36137
36370
  const match = flags.match;
36138
36371
  const count = flags.count;
36139
36372
  const includeRetracted = flags["include-retracted"];
36140
36373
  const whereRaw = flags.where ?? [];
36141
36374
  const where = whereRaw.map(parseWhereFlag);
36375
+ if (ctx.liveMode && sinceRepoSeq !== undefined) {
36376
+ usageError("--since-repo-seq cannot be used with --live.", "wh thing list --since-repo-seq 42 --all --format json");
36377
+ }
36142
36378
  if (count) {
36143
36379
  if (cursor || all || limit || ctx.liveMode) {
36144
- usageError("Usage: wh thing list --count [--shape SHAPE] [--kind KIND] [--match PATTERN]", "wh thing list --shape Player --count");
36380
+ usageError("Usage: wh thing list --count [--shape SHAPE] [--kind KIND] [--match PATTERN] [--since-repo-seq N]", "wh thing list --shape Player --count --since-repo-seq 42");
36145
36381
  }
36146
36382
  const componentRef2 = flags.component;
36147
36383
  const strictExclude2 = !!flags["exclude-components"];
@@ -36154,7 +36390,8 @@ var handleHead = async (ctx, { flags, args }) => {
36154
36390
  componentRef: componentRef2,
36155
36391
  excludeComponents: strictExclude2,
36156
36392
  excludeInfraShapes: !strictExclude2 && !shape,
36157
- where: where.length > 0 ? where : undefined
36393
+ where: where.length > 0 ? where : undefined,
36394
+ ...sinceRepoSeq === undefined ? {} : { sinceRepoSeq }
36158
36395
  });
36159
36396
  }
36160
36397
  if (cursor && !limit) {
@@ -36181,7 +36418,8 @@ var handleHead = async (ctx, { flags, args }) => {
36181
36418
  componentRef,
36182
36419
  excludeComponents,
36183
36420
  excludeInfraShapes,
36184
- where: where.length > 0 ? where : undefined
36421
+ where: where.length > 0 ? where : undefined,
36422
+ ...sinceRepoSeq === undefined ? {} : { sinceRepoSeq }
36185
36423
  };
36186
36424
  if (ctx.liveMode) {
36187
36425
  await runLive({
@@ -36214,7 +36452,8 @@ var handleHead = async (ctx, { flags, args }) => {
36214
36452
  componentRef,
36215
36453
  excludeComponents,
36216
36454
  excludeInfraShapes,
36217
- where: where.length > 0 ? where : undefined
36455
+ where: where.length > 0 ? where : undefined,
36456
+ ...sinceRepoSeq === undefined ? {} : { sinceRepoSeq }
36218
36457
  }, streamJsonl ? async (items) => {
36219
36458
  writePageOutput(ctx, items, { limit: pageLimit }, () => {});
36220
36459
  return await ctx.flushOut?.() ?? true;
@@ -36228,23 +36467,25 @@ var handleHead = async (ctx, { flags, args }) => {
36228
36467
  componentRef,
36229
36468
  excludeComponents,
36230
36469
  excludeInfraShapes,
36231
- where: where.length > 0 ? where : undefined
36470
+ where: where.length > 0 ? where : undefined,
36471
+ ...sinceRepoSeq === undefined ? {} : { sinceRepoSeq }
36232
36472
  });
36233
36473
  if (streamJsonl)
36234
36474
  return;
36475
+ requireIncrementalCheckpoint(result.repoSeq, sinceRepoSeq !== undefined && !result.nextCursor);
36235
36476
  if (!all && result.nextCursor) {
36236
36477
  emitPartialPageHint(ctx, (result.items ?? []).length, result.nextCursor, boundedLimit);
36237
36478
  }
36238
36479
  writePageOutput(ctx, result.items ?? [], {
36239
36480
  limit: all ? pageLimit : boundedLimit,
36240
- nextCursor: result.nextCursor ?? null
36481
+ nextCursor: result.nextCursor ?? null,
36482
+ ...result.repoSeq === undefined ? {} : { repoSeq: result.repoSeq }
36241
36483
  }, () => renderHead(ctx.out, ctx.colors, ctx.chars, result, org, repo, shape, kind));
36242
36484
  };
36243
36485
  async function fetchAllHeadPages(ctx, org, repo, opts, onPage) {
36244
- const items = [];
36245
- let cursor = opts.cursor;
36246
- while (true) {
36247
- const page = await ctx.client.thing.head(org, repo, {
36486
+ return collectThingPages({
36487
+ initialCursor: opts.cursor,
36488
+ fetchPage: (cursor) => ctx.client.thing.head(org, repo, {
36248
36489
  shape: opts.shape,
36249
36490
  kind: opts.kind,
36250
36491
  match: opts.match,
@@ -36254,20 +36495,13 @@ async function fetchAllHeadPages(ctx, org, repo, opts, onPage) {
36254
36495
  componentRef: opts.componentRef,
36255
36496
  excludeComponents: opts.excludeComponents,
36256
36497
  excludeInfraShapes: opts.excludeInfraShapes,
36257
- where: opts.where
36258
- });
36259
- const pageItems = page.items ?? [];
36260
- if (onPage) {
36261
- if (!await onPage(pageItems))
36262
- break;
36263
- } else {
36264
- items.push(...pageItems);
36265
- }
36266
- if (!page.nextCursor)
36267
- break;
36268
- cursor = page.nextCursor;
36269
- }
36270
- return { items, nextCursor: undefined };
36498
+ where: opts.where,
36499
+ ...opts.sinceRepoSeq === undefined ? {} : { sinceRepoSeq: opts.sinceRepoSeq }
36500
+ }),
36501
+ onPage,
36502
+ requireRepoSeq: opts.sinceRepoSeq !== undefined,
36503
+ title: "Thing list"
36504
+ });
36271
36505
  }
36272
36506
 
36273
36507
  // ../../packages/warmhub-cli/src/domains/thing/query.ts
@@ -36279,6 +36513,9 @@ var queryFlags = {
36279
36513
  description: "Max results per page (default: 50, max: 500)"
36280
36514
  }),
36281
36515
  cursor: flag.string({ description: "Opaque pagination cursor" }),
36516
+ "since-repo-seq": flag.number({
36517
+ description: "Return identities changed after this repository sequence"
36518
+ }),
36282
36519
  all: flag.boolean({ description: "Fetch all pages" }),
36283
36520
  match: flag.string({ description: "Filter by wref glob pattern" }),
36284
36521
  count: flag.boolean({ description: "Return count of matching items" }),
@@ -36309,6 +36546,7 @@ var handleQuery = async (ctx, { flags }) => {
36309
36546
  const kind = validateKind(flags.kind);
36310
36547
  const limit = flags.limit;
36311
36548
  const cursor = flags.cursor;
36549
+ const sinceRepoSeq = parseSinceRepoSeqFlag(flags["since-repo-seq"]);
36312
36550
  const all = flags.all;
36313
36551
  const match = flags.match;
36314
36552
  const count = flags.count;
@@ -36324,9 +36562,12 @@ var handleQuery = async (ctx, { flags }) => {
36324
36562
  const c = ctx.colors;
36325
36563
  const whereRaw = flags.where ?? [];
36326
36564
  const where = whereRaw.map(parseWhereFlag);
36565
+ if (ctx.liveMode && sinceRepoSeq !== undefined) {
36566
+ usageError("--since-repo-seq cannot be used with --live.", "wh thing query --since-repo-seq 42 --all --format json");
36567
+ }
36327
36568
  if (count) {
36328
36569
  if (cursor || all || limit || ctx.liveMode || role) {
36329
- usageError("Usage: wh thing query --count [--shape SHAPE] [--about WREF] [--kind KIND] [--match PATTERN]", "wh thing query --kind assertion --about Player/alice --count");
36570
+ usageError("Usage: wh thing query --count [--shape SHAPE] [--about WREF] [--kind KIND] [--match PATTERN] [--since-repo-seq N]", "wh thing query --kind assertion --about Player/alice --count --since-repo-seq 42");
36330
36571
  }
36331
36572
  return handleCount(ctx, org, repo, {
36332
36573
  shape,
@@ -36338,7 +36579,8 @@ var handleQuery = async (ctx, { flags }) => {
36338
36579
  componentRef,
36339
36580
  excludeComponents,
36340
36581
  excludeInfraShapes,
36341
- where: where.length > 0 ? where : undefined
36582
+ where: where.length > 0 ? where : undefined,
36583
+ ...sinceRepoSeq === undefined ? {} : { sinceRepoSeq }
36342
36584
  });
36343
36585
  }
36344
36586
  if (role && (!about || !resolveCollections)) {
@@ -36365,7 +36607,8 @@ var handleQuery = async (ctx, { flags }) => {
36365
36607
  componentRef,
36366
36608
  excludeComponents,
36367
36609
  excludeInfraShapes,
36368
- where: where.length > 0 ? where : undefined
36610
+ where: where.length > 0 ? where : undefined,
36611
+ ...sinceRepoSeq === undefined ? {} : { sinceRepoSeq }
36369
36612
  };
36370
36613
  if (ctx.liveMode) {
36371
36614
  await runLive({
@@ -36401,7 +36644,8 @@ var handleQuery = async (ctx, { flags }) => {
36401
36644
  componentRef,
36402
36645
  excludeComponents,
36403
36646
  excludeInfraShapes,
36404
- where: where.length > 0 ? where : undefined
36647
+ where: where.length > 0 ? where : undefined,
36648
+ ...sinceRepoSeq === undefined ? {} : { sinceRepoSeq }
36405
36649
  }, streamJsonl ? async (items) => {
36406
36650
  writePageOutput(ctx, items, { limit: pageLimit }, () => {});
36407
36651
  return await ctx.flushOut?.() ?? true;
@@ -36418,23 +36662,25 @@ var handleQuery = async (ctx, { flags }) => {
36418
36662
  componentRef,
36419
36663
  excludeComponents,
36420
36664
  excludeInfraShapes,
36421
- where: where.length > 0 ? where : undefined
36665
+ where: where.length > 0 ? where : undefined,
36666
+ ...sinceRepoSeq === undefined ? {} : { sinceRepoSeq }
36422
36667
  });
36423
36668
  if (streamJsonl)
36424
36669
  return;
36670
+ requireIncrementalCheckpoint(result.repoSeq, sinceRepoSeq !== undefined && !result.nextCursor);
36425
36671
  if (!all && result.nextCursor) {
36426
36672
  emitPartialPageHint(ctx, (result.items ?? []).length, result.nextCursor, boundedLimit);
36427
36673
  }
36428
36674
  writePageOutput(ctx, result.items ?? [], {
36429
36675
  limit: all ? pageLimit : boundedLimit,
36430
- nextCursor: result.nextCursor ?? null
36676
+ nextCursor: result.nextCursor ?? null,
36677
+ ...result.repoSeq === undefined ? {} : { repoSeq: result.repoSeq }
36431
36678
  }, () => renderQueryResults(ctx.out, c, result));
36432
36679
  };
36433
36680
  async function fetchAllQueryPages(ctx, org, repo, opts, onPage) {
36434
- const items = [];
36435
- let cursor = opts.cursor;
36436
- while (true) {
36437
- const page = await ctx.client.thing.query(org, repo, {
36681
+ return collectThingPages({
36682
+ initialCursor: opts.cursor,
36683
+ fetchPage: (cursor) => ctx.client.thing.query(org, repo, {
36438
36684
  shape: opts.shape,
36439
36685
  about: opts.about,
36440
36686
  kind: opts.kind,
@@ -36447,20 +36693,13 @@ async function fetchAllQueryPages(ctx, org, repo, opts, onPage) {
36447
36693
  componentRef: opts.componentRef,
36448
36694
  excludeComponents: opts.excludeComponents,
36449
36695
  excludeInfraShapes: opts.excludeInfraShapes,
36450
- where: opts.where
36451
- });
36452
- const pageItems = page.items ?? [];
36453
- if (onPage) {
36454
- if (!await onPage(pageItems))
36455
- break;
36456
- } else {
36457
- items.push(...pageItems);
36458
- }
36459
- if (!page.nextCursor)
36460
- break;
36461
- cursor = page.nextCursor;
36462
- }
36463
- return { items, nextCursor: undefined };
36696
+ where: opts.where,
36697
+ ...opts.sinceRepoSeq === undefined ? {} : { sinceRepoSeq: opts.sinceRepoSeq }
36698
+ }),
36699
+ onPage,
36700
+ requireRepoSeq: opts.sinceRepoSeq !== undefined,
36701
+ title: "Thing query"
36702
+ });
36464
36703
  }
36465
36704
  function renderQueryResults(out, c, result) {
36466
36705
  const items = result.items ?? [];
@@ -36629,6 +36868,9 @@ var retractFlags2 = {
36629
36868
  description: "Optional safety check: thing|assertion|shape|collection (errors on mismatch)"
36630
36869
  }),
36631
36870
  reason: flag.string({ description: "Reason for retraction (<=500 chars)" }),
36871
+ "expected-version": flag.number({
36872
+ description: "only retract if the target is still at this version (optimistic concurrency)"
36873
+ }),
36632
36874
  message: flag.string({ short: "m", description: "Commit message" }),
36633
36875
  committer: flag.string({
36634
36876
  description: "Committer wref (shape or shaped thing; e.g. Agent/bot-1)"
@@ -36640,13 +36882,14 @@ var retractFlags2 = {
36640
36882
  var handleThingRetract = async (ctx, { flags, args }) => {
36641
36883
  const name = args[0];
36642
36884
  if (!name) {
36643
- usageError("Usage: wh thing retract <wref> -m <message> [--reason <text>] [--kind thing|assertion|shape|collection]", 'wh thing retract Measurement/m1 -m "cleanup" --reason "bad source"');
36885
+ usageError("Usage: wh thing retract <wref> -m <message> [--reason <text>] [--kind thing|assertion|shape|collection] [--expected-version <n>]", 'wh thing retract Measurement/m1 -m "cleanup" --reason "bad source"');
36644
36886
  }
36645
36887
  const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
36646
36888
  const committer = flags.committer;
36647
36889
  const message = flags.message;
36648
36890
  const kind = flags.kind;
36649
36891
  const reason = flags.reason;
36892
+ const expectedVersion = parsePositiveIntFlag(flags["expected-version"], "--expected-version", "wh thing retract Player/alice --expected-version 3");
36650
36893
  const leaseId = requireLeaseIdFlag(flags["lease-id"], "wh thing retract Player/alice --lease-id <id>");
36651
36894
  const c = ctx.colors;
36652
36895
  const result = await ctx.client.commit.apply(org, repo, message ?? `retract ${name}`, [
@@ -36655,6 +36898,7 @@ var handleThingRetract = async (ctx, { flags, args }) => {
36655
36898
  name,
36656
36899
  ...kind ? { kind } : {},
36657
36900
  ...reason ? { reason } : {},
36901
+ ...expectedVersion !== undefined ? { expectedVersion } : {},
36658
36902
  ...leaseId ? { leaseId } : {}
36659
36903
  }
36660
36904
  ], { committer });
@@ -36842,6 +37086,9 @@ async function fetchAllSearchPages(ctx, org, repo, queryText, opts) {
36842
37086
  // ../../packages/warmhub-cli/src/domains/thing/view.ts
36843
37087
  import { readFileSync as readFileSync7 } from "node:fs";
36844
37088
 
37089
+ // ../../packages/rules/src/read-limits.ts
37090
+ var MAX_GET_MANY_WREFS2 = 500;
37091
+
36845
37092
  // ../../packages/warmhub-cli/src/io.ts
36846
37093
  function isBrokenPipe(error) {
36847
37094
  return error instanceof Error && "code" in error && error.code === "EPIPE";
@@ -37078,8 +37325,8 @@ var handleView = async (ctx, { flags, args, terminator }) => {
37078
37325
  if (wrefs.length === 0) {
37079
37326
  throw new CliError(2 /* UserInput */, "USER_INPUT", "Missing required <wref>. Pass a wref as a positional arg, via --file <path>, or pipe newline-delimited wrefs on stdin.", undefined, "Example: wh thing view Game/base --depth 2");
37080
37327
  }
37081
- if (wrefs.length > MAX_GET_MANY_WREFS) {
37082
- throw new CliError(2 /* UserInput */, "USER_INPUT", `Too many wrefs: ${wrefs.length} (max ${MAX_GET_MANY_WREFS} per call).`, undefined, `Split your input into chunks of ${MAX_GET_MANY_WREFS} or fewer.`);
37328
+ if (wrefs.length > MAX_GET_MANY_WREFS2) {
37329
+ throw new CliError(2 /* UserInput */, "USER_INPUT", `Too many wrefs: ${wrefs.length} (max ${MAX_GET_MANY_WREFS2} per call).`, undefined, `Split your input into chunks of ${MAX_GET_MANY_WREFS2} or fewer.`);
37083
37330
  }
37084
37331
  const isBatch = wrefs.length > 1 || flags.file !== undefined;
37085
37332
  if (isBatch && flags.depth !== undefined) {
@@ -39236,7 +39483,7 @@ var createFlags3 = {
39236
39483
  description: "collection members (comma-separated wrefs)"
39237
39484
  }),
39238
39485
  "expected-version": flag.number({
39239
- description: "Only apply if the --revise target is still at this version (optimistic concurrency). Requires --revise."
39486
+ description: "only apply if the --revise or single --retract target is still at this version (optimistic concurrency)."
39240
39487
  }),
39241
39488
  "lease-id": flag.string({
39242
39489
  description: "read-lease token from `wh thing lease`, bound to --revise or one --retract target (auto-released on success)"
@@ -39499,7 +39746,7 @@ function buildAddOperations(input) {
39499
39746
  });
39500
39747
  }
39501
39748
  function buildRetractOperations(input) {
39502
- const { retractNames, kinds, reasons, leaseId } = input;
39749
+ const { retractNames, kinds, reasons, expectedVersion, leaseId } = input;
39503
39750
  if (leaseId !== undefined && retractNames.length !== 1) {
39504
39751
  throw new CliError(2 /* UserInput */, "USER_INPUT", `--lease-id requires exactly one --retract target; received ${retractNames.length}.`, undefined, "wh commit submit --retract Player/alice --lease-id lease-xyz --repo acme/world");
39505
39752
  }
@@ -39529,6 +39776,7 @@ function buildRetractOperations(input) {
39529
39776
  name,
39530
39777
  ...kind ? { kind } : {},
39531
39778
  ...reason ? { reason } : {},
39779
+ ...expectedVersion !== undefined ? { expectedVersion } : {},
39532
39780
  ...leaseId ? { leaseId } : {}
39533
39781
  };
39534
39782
  });
@@ -39628,15 +39876,14 @@ import { createInterface as createInterface2 } from "node:readline";
39628
39876
  class JsonlPartialSubmissionError extends CliError {
39629
39877
  }
39630
39878
  function jsonlPartialSubmissionError(args) {
39631
- const { acknowledgedOperationCount, cause } = args;
39632
- if (args.failedAppendMayHaveLanded === true) {
39633
- const backendCode = toWarmHubError(cause).backendCode;
39634
- const suffix = backendCode ? ` (backend: ${backendCode})` : "";
39635
- return new JsonlPartialSubmissionError(4 /* Backend */, "BACKEND", `Stream append failed after ${acknowledgedOperationCount} acknowledged operation(s).${suffix}`, cause, "The failed append may also have landed; inspect repository state before submitting any remaining JSONL operations.", undefined, backendCode);
39636
- }
39637
- const message = cause instanceof Error ? cause.message : String(cause);
39638
- const causeHint = cause instanceof CliError && cause.hint ? `${cause.hint} ` : "";
39639
- return new JsonlPartialSubmissionError(4 /* Backend */, "BACKEND", `${message} (${acknowledgedOperationCount} earlier operation(s) already acknowledged by the server before this input failure).`, cause, `${causeHint}Earlier chunks have landed; inspect repository state before submitting any remaining JSONL operations.`);
39879
+ const receipt = buildStreamRecoveryReceipt({
39880
+ acknowledgedOperationCount: args.acknowledgedOperationCount,
39881
+ lastAcknowledgedRepoSeq: args.lastAcknowledgedRepoSeq,
39882
+ cause: args.cause,
39883
+ attemptedAppendOutcome: args.failedAppendMayHaveLanded === true ? "unknown" : "not_applied"
39884
+ });
39885
+ const causeHint = args.cause instanceof CliError && args.cause.hint ? `${args.cause.hint} ` : "";
39886
+ return new JsonlPartialSubmissionError(4 /* Backend */, "BACKEND", receipt.message, args.cause, `${causeHint}${receipt.hint}`, undefined, receipt.errorCode, receipt.recovery);
39640
39887
  }
39641
39888
 
39642
39889
  // ../../packages/warmhub-cli/src/domains/commit-submit-utils.ts
@@ -39721,50 +39968,6 @@ function toCommitSubmitResult(args) {
39721
39968
  operations
39722
39969
  };
39723
39970
  }
39724
- function backendErrorCode(error) {
39725
- if (typeof error !== "object" || error === null) {
39726
- return;
39727
- }
39728
- const typed = error;
39729
- if (typeof typed.data === "object" && typed.data !== null) {
39730
- const data = typed.data;
39731
- if (typeof data.code === "string") {
39732
- return data.code;
39733
- }
39734
- }
39735
- const causeCode = backendErrorCode(typed.cause);
39736
- if (causeCode !== undefined) {
39737
- return causeCode;
39738
- }
39739
- return typeof typed.code === "string" ? typed.code : undefined;
39740
- }
39741
- function backendErrorStatus(error) {
39742
- if (typeof error !== "object" || error === null) {
39743
- return;
39744
- }
39745
- const typed = error;
39746
- if (typeof typed.status === "number") {
39747
- return typed.status;
39748
- }
39749
- if (typeof typed.data === "object" && typed.data !== null) {
39750
- const data = typed.data;
39751
- if (typeof data.status === "number") {
39752
- return data.status;
39753
- }
39754
- if (typeof data.httpStatus === "number") {
39755
- return data.httpStatus;
39756
- }
39757
- if (typeof data.warmhub?.status === "number") {
39758
- return data.warmhub.status;
39759
- }
39760
- }
39761
- return backendErrorStatus(typed.cause);
39762
- }
39763
- function firstJsonlAppendErrorMayHaveCommitted(error) {
39764
- const errorCode = backendErrorCode(error);
39765
- const errorStatus = backendErrorStatus(error);
39766
- return errorCode === undefined || errorCode === "COMMIT_OUTCOME_UNKNOWN" || errorCode === "INTERNAL_ERROR" || errorCode === "NETWORK" || errorCode === "SERVICE_UNAVAILABLE" || errorCode === "BACKEND" && errorStatus !== undefined && errorStatus >= 500;
39767
- }
39768
39971
 
39769
39972
  // ../../packages/warmhub-cli/src/domains/stream-progress.ts
39770
39973
  import { createReadStream } from "node:fs";
@@ -39949,12 +40152,12 @@ async function applyJsonlCommit(ctx, args) {
39949
40152
  const tAppendStart = performance.now();
39950
40153
  progress.start(t0);
39951
40154
  let opCount = 0;
40155
+ let repoSeq;
39952
40156
  try {
39953
40157
  const operations = [];
39954
40158
  const submittedOperations = [];
39955
40159
  let allocatedTokenRanges = [];
39956
40160
  let createdByEmail;
39957
- let repoSeq;
39958
40161
  let parsedOpCount = 0;
39959
40162
  let chunk = [];
39960
40163
  let lineNumber = 0;
@@ -39977,11 +40180,13 @@ async function applyJsonlCommit(ctx, args) {
39977
40180
  });
39978
40181
  } catch (cause) {
39979
40182
  const completed = opCount;
39980
- if (opCount > 0 || firstJsonlAppendErrorMayHaveCommitted(cause)) {
40183
+ const failedAppendMayHaveLanded = !streamAppendWasExplicitlyRejected(cause);
40184
+ if (opCount > 0 || failedAppendMayHaveLanded) {
39981
40185
  throw jsonlPartialSubmissionError({
39982
40186
  acknowledgedOperationCount: completed,
39983
40187
  cause,
39984
- failedAppendMayHaveLanded: true
40188
+ failedAppendMayHaveLanded,
40189
+ lastAcknowledgedRepoSeq: repoSeq
39985
40190
  });
39986
40191
  }
39987
40192
  throw cause;
@@ -40176,7 +40381,8 @@ async function applyJsonlCommit(ctx, args) {
40176
40381
  }
40177
40382
  throw jsonlPartialSubmissionError({
40178
40383
  acknowledgedOperationCount: opCount,
40179
- cause: error
40384
+ cause: error,
40385
+ lastAcknowledgedRepoSeq: repoSeq
40180
40386
  });
40181
40387
  }
40182
40388
  }
@@ -40246,9 +40452,10 @@ var handleSubmit = async (ctx, { flags, args }) => {
40246
40452
  const abouts = flags.about ?? [];
40247
40453
  const reasons = flags.reason ?? [];
40248
40454
  const kinds = flags.kind ?? [];
40249
- const expectedVersion = parsePositiveIntFlag(flags["expected-version"], "--expected-version", "wh commit submit --revise Player/alice --data '{...}' --expected-version 3");
40250
- if (expectedVersion !== undefined && !reviseName) {
40251
- usageError("--expected-version requires --revise", "wh commit submit --revise Player/alice --data '{...}' --expected-version 3");
40455
+ const expectedVersionExample = retractNames.length > 0 ? "wh commit submit --retract Player/alice --expected-version 3" : "wh commit submit --revise Player/alice --data '{...}' --expected-version 3";
40456
+ const expectedVersion = parsePositiveIntFlag(flags["expected-version"], "--expected-version", expectedVersionExample);
40457
+ if (expectedVersion !== undefined && reviseName === undefined && retractNames.length !== 1) {
40458
+ usageError("--expected-version requires --revise or exactly one --retract", "wh commit submit --retract Player/alice --expected-version 3");
40252
40459
  }
40253
40460
  const leaseIdFlag = requireLeaseIdFlag(flags["lease-id"], `wh commit submit --revise Player/alice --data '{"score":2}' --lease-id <id>`);
40254
40461
  if (leaseIdFlag !== undefined && reviseName === undefined && retractNames.length === 0) {
@@ -40372,6 +40579,7 @@ var handleSubmit = async (ctx, { flags, args }) => {
40372
40579
  retractNames,
40373
40580
  kinds,
40374
40581
  reasons,
40582
+ expectedVersion,
40375
40583
  leaseId: leaseIdFlag
40376
40584
  });
40377
40585
  } else if (operationSource === "--revise") {
@@ -44293,7 +44501,7 @@ var ORG_DOMAIN = defineDomain({
44293
44501
  });
44294
44502
 
44295
44503
  // ../../packages/warmhub-cli/src/domains/prime-content.md
44296
- var prime_content_default = "# WarmHub CLI Context\n> **Context Recovery**: Run `wh prime` after compaction or new session\n\n## Environment\n{{REPO_LINE}}\n\n## Core Concepts\n- **Thing**: A named entity versioned by writes. **Assertion**: A thing that makes a shape-validated claim about another thing.\n- **Shape**: A thing defining data structure; every other thing has one. **Write**: One or more add/revise/retract operations with per-operation results.\n- **wref**: A reference to a thing. Local: `Player` (the shape) or `Player/alice` (a thing with that shape). Cross-repo: `wh:org/repo/Shape` or `wh:org/repo/Shape/name`.\n\n## Versioned Wrefs\n- `Shape` addresses the shape itself; `Shape/name` addresses a thing with that shape. `@vN` pins either.\n- Floating write refs to retracted targets fail; existing pinned versions remain valid.\n- Rename invalidates old spellings, including `@vN`; the new name resolves history.\n- Untyped wrefs accept any thing. `wref<T>` requires the target's shape to be `T`; a shape has no shape, so never satisfies it. `wref?` coalesces only `thing_absent`, never a missing shape.\n\n## Key Workflows\n\n**Write data** (discover shapes → scaffold ops → submit):\n```bash\nwh shape list --repo org/repo # list available shapes\nwh shape view ShapeName --repo org/repo # inspect fields\nwh shape template ShapeName -o ops.json --repo org/repo # scaffold write ops (supports --kind assertion)\n# edit ops.json — fill FILL_IN placeholders — then:\nwh commit submit --file ops.json -m \"msg\" --repo org/repo # submit operations (bare `wh commit` also works)\n# or single assertion (no file needed):\nwh assertion create --shape ShapeName --about Target/name --name my-assertion --data '{\"field\":1}' --repo org/repo\n# Relay failed operation details when present. Never hand-guess ops JSON — use `wh shape template <Shape>`.\n```\n\n**Read data:**\n```bash\nwh thing list --repo org/repo # all things at HEAD\nwh thing view Shape/name --repo org/repo # inspect a thing\nwh thing query --shape MyShape --repo org/repo # find things by shape\nwh thing about Shape --repo org/repo # assertions about a shape\nwh assertion list --repo org/repo # all assertions at HEAD\nwh thing history Shape/name --repo org/repo # version history\n\n# Batch read — wh thing view is variadic (max 500 wrefs/call):\nwh thing view Player/alice Player/bob # variadic positionals\nwh thing view --file wrefs.txt --json # one wref per line\ncat wrefs.txt | wh thing view --format jsonl # one JSON line per *deduped* requested wref\n```\n\n## Wref Quick Reference\n\nWrites use explicit names and wrefs. Untyped fields, collection members,\nassertion `about`, and committers accept any thing. Create a deterministic\ntarget before referencing it in the same commit.\n\n## Command Reference\n\n**Global flags**: `--repo`, `--format`, `--json`, `--live`\n### thing — Thing operations\n- `wh thing list [--shape] [--kind] [--match] [--include-retracted]` — Current HEAD state\n- `wh thing view [<wref>...] [--file <path>] [--version] [--depth] [--include-retracted] [--data-mode auto|full]` — Thing details. Variadic (max 500). `--version` implies `--include-retracted`. Batch JSON returns `{ requested, items, missing }`; jsonl emits one row per deduped requested wref. Large Set/List bodies summarize by default; use `--data-mode full` for canonical collection JSON.\n- `wh thing history [wref] [--shape] [--about] [--include-retracted]` — Version history\n- `wh thing resolve <wref>` — Resolve a wref to its canonical thing identity\n- `wh thing create <name|Shape/name> (--data|--file) [--shape]` — Create\n- `wh thing revise <name> [--data] [--message] [--committer] [--expected-version]` — Revise (CONFLICT if HEAD≠n)\n- `wh thing retract <wref> -m <message> [--reason] [--kind]` — Retract\n- `wh thing query [--shape] [--kind] [--about] [--match]` — Query by filters\n- `wh thing search <query> [--shape] [--kind] [--about] [--mode]` — Search text\n- `wh thing rename <Shape/oldName> <newName>` — Rename\n- `wh thing refs <wref> [--inbound] [--outbound] [--field]` — Show field references; use `wh thing about` for assertions about the target thing\n- `wh thing about <wref> [--shape] [--match] [--depth] [--resolve-collections] [--role from|to|ends] [--limit] [--include-retracted]` — Show assertions about the target identity; `--resolve-collections` expands bare/@HEAD/@ALL members, `--role` filters direction; not pinned @vN\n\n### commit — Write operations\n- `wh commit submit [--ops|--file|--add|--revise|--retract] [--kind] [--data] [--reason] [--expected-version] [--chunk-size] [--skip-existing] [--progress] [--stream-id]` — Submit operations (bare `wh commit` is equivalent). Use `--file ops.jsonl --stream-id <id> --chunk-size 5000 --skip-existing` for bulk ingest.\n- `wh shape template <shape> [shape2 ...] [--kind] [--operation add|revise|retract] [-o file]` — Generate sample ops\n\n### assertion — Assertion operations\n- `wh assertion list [--about wref] [--shape] [--match] [--include-retracted]` — Browse assertions\n- `wh assertion view <wref> [--version] [--include-retracted]` — Assertion details\n- `wh assertion create --shape <s> --name <n> --about <wref> [--data] [-m] [--committer]` — Create assertion\n- `wh assertion revise <wref> --data <json> [--message] [--committer]` — Revise assertion\n- `wh assertion retract <wref> -m <message> [--reason] [--committer]` — Retract assertion\n- `wh assertion history <wref> [--include-retracted]` — Assertion history\n\n### shape — Shape management\n- `wh shape list [--match] [--include-retracted]` — List all shapes\n- `wh shape view <name> [--include-retracted]` — Shape details\n- `wh shape revise <name> (--fields|--file)`\n- `wh shape create <name> (--fields|--file)`\n- `wh shape retract <name> -m <message> [--reason]` — Retract shape\n- `wh shape history <name> [--include-retracted]` — Shape history\n- `wh shape rename <oldName> <newName>` — Rename shape\n\n### repo — Repository management\n- `wh repo create <org/name> [--display-name] [--description] [--visibility]` — Create repo\n- `wh repo list [org]` — List repos\n- `wh repo view [org/repo]` — Repo details\n\n### org — Organization management\n- `wh org create <name> [--display-name]` — Create a new organization\n- `wh org view <name>` — View organization details (alias: info)\n- `wh org list` — List all organizations\n\n### sub — Subscription management\n- `wh sub create <name> [flags]` — Create a subscription\n- `wh sub view <name>` — View subscription details\n- `wh sub list` — List all subscriptions\n- `wh sub log <name>` — Tail subscription delivery feed\n- `wh sub attempts <runId>` — Show attempt history for a run\n- `wh sub pause <name>` — Pause a subscription\n- `wh sub resume <name>` — Resume a paused subscription\n- `wh sub bind <name> [--credentials]` — Bind a credential set to a subscription for webhook auth\n- `wh sub unbind <name>` — Remove credential binding from a subscription\n- `wh sub delete <name>` — Delete a subscription\n\n### notifications — Action notification listing\n- `wh notifications [--limit] [--since]` — List repo-scoped action notifications\n\n### credential — Credential set management\n- `wh credential create <name> [--repo org/repo | --org org] [--scope] [--description]` — Create an empty credential set\n- `wh credential list [--repo org/repo | --org org]` — List credential sets accessible from a repo or org\n- `wh credential view <name> [--repo org/repo | --org org]` — View a credential set (key names only, no values)\n- `wh credential delete <name> [--repo org/repo | --org org]` — Delete a credential set and its Vault object\n- `wh credential set <setName> [<keyName>] [--repo org/repo | --org org] [--value]` — Set credential key(s). With `<keyName>`: single-key form (reads value from `--value` or stdin). Without `<keyName>`: batch form (reads JSON object from stdin, e.g. `{\"KEY\":\"val\"}`)\n- `wh credential unset <setName> <keyName> [--repo org/repo | --org org]` — Remove a key from a credential set\n- `wh credential audit <setName> [--repo org/repo | --org org]` — View audit log for a credential set\n- `wh credential revoke <setName> [--repo org/repo | --org org] [--reason]` — Revoke a credential set (blocks new binds and stops bound webhook deliveries)\n\n### component — Component management\n- `wh component validate <path>` — Validate package\n- `wh component install <org/name>` — Install a registered component\n- `wh component register <name> --org <org> --manifest <path> [flags]` — Register component identity\n- `wh component unregister <org/name>` — Remove a registered component identity\n- `wh component registry list --org <org>` — List registered components\n- `wh component registry view <org/name>` — View a registered component\n- `wh component registry update <org/name> [flags]` — Update a registered component\n- `wh component list` — List installed components\n- `wh component update <org/name>` — Update installed component\n- `wh component view <org/name>` — Show component details (alias: show)\n- `wh component doctor <org/name>` — Run component health checks\n- `wh component teardown <org/name>` — Pause component subscriptions\n\n### Getting More Info\n- `wh help` — help overview\n- `wh <domain>` — domain verbs\n- `wh <domain> <verb> --help` — verb flags and examples\n- `wh help --format json` — `CliSpecV2` (`schemaVersion: 2`): global/contextual/root tables and command overrides\n\n## Common Workflows\n\n**Explore a repo:**\n```bash\nwh thing list --repo org/repo # see all things in HEAD\nwh thing view Shape/name --repo org/repo # inspect a specific thing\nwh thing history Shape/name --repo org/repo # inspect version history\nwh thing about Shape/name # assertions about thing/shape\n```\n\n**Create an assertion** (most common write):\n```bash\n# --about takes an untyped target wref: Shape or Shape/name.\nwh assertion create --shape MyShape --about TargetShape/target-name \\\n --name my-assertion --data '{\"field_a\":1,\"field_b\":\"value\"}' --repo org/repo\n# Output includes per-operation status; relay failures when present.\n```\n\n**Create via write entrypoint** (alternative, supports batches and streams):\n```bash\nwh commit submit --add my-item --shape MyShape --kind assertion \\\n --about TargetShape/target-name --data '{\"field_a\":1}' --repo org/repo\n```\n\n**Batch write via file** (generate template → edit → submit):\n```bash\nwh shape template Hypothesis Evidence -o ops.json # add --kind assertion --about TargetShape/name for assertions\n# edit ops.json — fill FILL_IN placeholders\nwh commit submit --file ops.json -m \"batch update\" # submit all operations (bare `wh commit` is equivalent)\n# --file format: docs.warmhub.ai/cli-reference/commit-operations\n```\n\n**Bulk write (JSONL stream, >1k ops)** — one chunked stream beats per-record commits (~60× faster):\n```bash\nwh shape template MyShape -o ops.jsonl # one op per line (.jsonl)\nID=\"bulk-$(date +%s)\" # caller id for chunk correlation\nwh commit submit --file ops.jsonl --stream-id \"$ID\" --chunk-size 5000 \\\n --skip-existing --progress -m \"bulk ingest\" --repo org/repo\n# --chunk-size: ops per append chunk (default 1000, max 10000); chunk≈1 is ~60× slower\n# --skip-existing: skips already-written add ops (drops per-row read-before-write)\n# Add-only restart: rerun the WHOLE file. --skip-existing makes fixed-name adds\n# idempotent; --stream-id only identifies the submission. Mid-stream resume is\n# not a CLI mode. Revise/retract JSONL is not full-rerun safe after an ambiguous\n# append; inspect repo state and reconcile.\n```\n\n**Create collections:**\n```bash\nwh commit submit --type arc --name route --members Location/a,Location/b --repo org/repo\nwh assertion create --shape Supports --name route-support --about Arc/route --data '{\"confidence\":0.8}' --repo org/repo\n```\n\n**Modify data:**\n```bash\nwh thing revise Shape/name --data '{\"x\":5,\"y\":3}' -m \"update\" --repo org/repo\nwh thing retract Shape/old-item -m \"withdrawn\" --reason \"data feed contaminated\" --repo org/repo\n```\n\n**Query and filter:**\n```bash\nwh thing query --shape MyShape # by shape\nwh thing query --kind assertion --about Shape/name # by kind + target\nwh thing history Shape/name --limit 10 # version history\n```\n\n## Built-in Content shape\n\nWarmHub repos expose three well-known content wrefs:\n- `Content/Readme` — stored markdown for humans\n- `Content/Agents` — stored markdown guidance for AI agents\n- `Content/LlmsTxt` — synthesized per-request sitemap (read-only)\n\nFetch via `wh repo content get --kind readme|agents|llms-txt`,\n`client.repo.getReadme/getAgents/getLlmsTxt`, MCP `warmhub_repo_content_get`,\nor raw HTTP `GET /{org}/{repo}/readme.md|agents.md|llms.txt`.\nSee `wh repo describe` → `additionalInformation` for the discovery field.\n\n## Query Discipline\n- Plan the repo, shapes, and wrefs you need before the first query.\n- Gather the needed facts from one repo before switching to another.\n- Do the queries first, then write one complete answer.\n\n## Agent Tips\n- **Always run commands for live data** — this context describes the CLI, not repo contents\n- **Before writing, discover wrefs** — run `wh thing list` or `wh shape list`\n- **Shape field types**: `string`, `number`, `boolean`, `wref`, arrays, optionals, nested objects\n- **Write commands return per-operation results** — relay failures and affected wrefs to the user\n- **Pass data inline** with `--data '{...}'` — do NOT create temp files\n- Add `--json` to any command for machine-readable JSON output\n- **Aliases teach canonical commands** — executing aliases emit a status hint; misleading lifecycle aliases reject and point to `retract`\n- Writes go through `wh commit submit` (or bare `wh commit`) or wrappers (`create`, `revise`, `retract`, `assertion create`). `retract` irreversibly withdraws an identity and creates a history entry.\n- Use `wh doctor` to check environment health\n";
44504
+ var prime_content_default = "# WarmHub CLI Context\n> **Context Recovery**: Run `wh prime` after compaction or new session\n\n## Environment\n{{REPO_LINE}}\n\n## Core Concepts\n- **Thing**: A named entity versioned by writes. **Assertion**: A thing that makes a shape-validated claim about another thing.\n- **Shape**: A thing defining data structure; every other thing has one. **Write**: One or more add/revise/retract operations with per-operation results.\n- **wref**: A reference to a thing. Local: `Player` (the shape) or `Player/alice` (a thing with that shape). Cross-repo: `wh:org/repo/Shape` or `wh:org/repo/Shape/name`.\n\n## Versioned Wrefs\n- `Shape` addresses the shape itself; `Shape/name` addresses a thing with that shape. `@vN` pins either.\n- Floating write refs to retracted targets fail; existing pinned versions remain valid.\n- Rename invalidates old spellings, including `@vN`; the new name resolves history.\n- Untyped wrefs accept any thing. `wref<T>` requires the target's shape to be `T`; a shape has no shape, so never satisfies it. `wref?` coalesces only `thing_absent`, never a missing shape.\n\n## Key Workflows\n\n**Write data** (discover shapes → scaffold ops → submit):\n```bash\nwh shape list --repo org/repo # list available shapes\nwh shape view ShapeName --repo org/repo # inspect fields\nwh shape template ShapeName -o ops.json --repo org/repo # scaffold write ops (supports --kind assertion)\n# edit ops.json — fill FILL_IN placeholders — then:\nwh commit submit --file ops.json -m \"msg\" --repo org/repo # submit operations (bare `wh commit` also works)\n# or single assertion (no file needed):\nwh assertion create --shape ShapeName --about Target/name --name my-assertion --data '{\"field\":1}' --repo org/repo\n# Relay failed operation details when present. Never hand-guess ops JSON — use `wh shape template <Shape>`.\n```\n\n**Read data:**\n```bash\nwh thing list --repo org/repo # all things at HEAD\nwh thing view Shape/name --repo org/repo # inspect a thing\nwh thing query --shape MyShape --repo org/repo # find things by shape\nwh thing about Shape --repo org/repo # assertions about a shape\nwh assertion list --repo org/repo # all assertions at HEAD\nwh thing history Shape/name --repo org/repo # version history\n\n# Batch read — wh thing view is variadic (max 500 wrefs/call):\nwh thing view Player/alice Player/bob # variadic positionals\nwh thing view --file wrefs.txt --json # one wref per line\ncat wrefs.txt | wh thing view --format jsonl # one JSON line per *deduped* requested wref\n```\n\n## Wref Quick Reference\n\nWrites use explicit names and wrefs. Untyped fields, collection members,\nassertion `about`, and committers accept any thing. Create a deterministic\ntarget before referencing it in the same commit.\n\n## Command Reference\n\n**Global flags**: `--repo`, `--format`, `--json`, `--live`\n### thing — Thing operations\n- `wh thing list [--shape] [--kind] [--match] [--include-retracted]` — Current HEAD state\n- `wh thing view [<wref>...] [--file <path>] [--version] [--depth] [--include-retracted] [--data-mode auto|full]` — Thing details. Variadic (max 500). `--version` implies `--include-retracted`. Batch JSON returns `{ requested, items, missing }`; jsonl emits one row per deduped requested wref. Large Set/List bodies summarize by default; use `--data-mode full` for canonical collection JSON.\n- `wh thing history [wref] [--shape] [--about] [--include-retracted]` — Version history\n- `wh thing resolve <wref>` — Resolve a wref to its canonical thing identity\n- `wh thing create <name|Shape/name> (--data|--file) [--shape]` — Create\n- `wh thing revise <name> [--data] [--message] [--committer] [--expected-version]` — Revise (CONFLICT if HEAD≠n)\n- `wh thing retract <wref> -m <message> [--reason] [--kind] [--expected-version]` — Retract\n- `wh thing query [--shape] [--kind] [--about] [--match]` — Query by filters\n- `wh thing search <query> [--shape] [--kind] [--about] [--mode]` — Search text\n- `wh thing rename <Shape/oldName> <newName>` — Rename\n- `wh thing refs <wref> [--inbound] [--outbound] [--field]` — Show field references; use `wh thing about` for assertions about the target thing\n- `wh thing about <wref> [--shape] [--match] [--depth] [--resolve-collections] [--role from|to|ends] [--limit] [--include-retracted]` — Show assertions about the target identity; `--resolve-collections` expands bare/@HEAD/@ALL members, `--role` filters direction; not pinned @vN\n\n### commit — Write operations\n- `wh commit submit [--ops|--file|--add|--revise|--retract] [--kind] [--data] [--reason] [--expected-version] [--chunk-size] [--skip-existing] [--progress] [--stream-id]` — Submit operations (bare `wh commit` is equivalent). Use `--file ops.jsonl --stream-id <id> --chunk-size 5000 --skip-existing` for bulk ingest.\n- `wh shape template <shape> [shape2 ...] [--kind] [--operation add|revise|retract] [-o file]` — Generate sample ops\n\n### assertion — Assertion operations\n- `wh assertion list [--about wref] [--shape] [--match] [--include-retracted]` — Browse assertions\n- `wh assertion view <wref> [--version] [--include-retracted]` — Assertion details\n- `wh assertion create --shape <s> --name <n> --about <wref> [--data] [-m] [--committer]` — Create assertion\n- `wh assertion revise <wref> --data <json> [--message] [--committer]` — Revise assertion\n- `wh assertion retract <wref> -m <message> [--reason] [--committer] [--expected-version]` — Retract assertion\n- `wh assertion history <wref> [--include-retracted]` — Assertion history\n\n### shape — Shape management\n- `wh shape list [--match] [--include-retracted]` — List all shapes\n- `wh shape view <name> [--include-retracted]` — Shape details\n- `wh shape revise <name> (--fields|--file)`\n- `wh shape create <name> (--fields|--file)`\n- `wh shape retract <name> -m <message> [--reason] [--expected-version]` — Retract shape\n- `wh shape history <name> [--include-retracted]` — Shape history\n- `wh shape rename <oldName> <newName>` — Rename shape\n\n### repo — Repository management\n- `wh repo create <org/name> [--display-name] [--description] [--visibility]` — Create repo\n- `wh repo list [org]` — List repos\n- `wh repo view [org/repo]` — Repo details\n\n### org — Organization management\n- `wh org create <name> [--display-name]` — Create a new organization\n- `wh org view <name>` — View organization details (alias: info)\n- `wh org list` — List all organizations\n\n### sub — Subscription management\n- `wh sub create <name> [flags]` — Create a subscription\n- `wh sub view <name>` — View subscription details\n- `wh sub list` — List all subscriptions\n- `wh sub log <name>` — Tail subscription delivery feed\n- `wh sub attempts <runId>` — Show attempt history for a run\n- `wh sub pause <name>` — Pause a subscription\n- `wh sub resume <name>` — Resume a paused subscription\n- `wh sub bind <name> [--credentials]` — Bind a credential set to a subscription for webhook auth\n- `wh sub unbind <name>` — Remove credential binding from a subscription\n- `wh sub delete <name>` — Delete a subscription\n\n### notifications — Action notification listing\n- `wh notifications [--limit] [--since]` — List repo-scoped action notifications\n\n### credential — Credential set management\n- `wh credential create <name> [--repo org/repo | --org org] [--scope] [--description]` — Create an empty credential set\n- `wh credential list [--repo org/repo | --org org]` — List credential sets accessible from a repo or org\n- `wh credential view <name> [--repo org/repo | --org org]` — View a credential set (key names only, no values)\n- `wh credential delete <name> [--repo org/repo | --org org]` — Delete a credential set and its Vault object\n- `wh credential set <setName> [<keyName>] [--repo org/repo | --org org] [--value]` — Set credential key(s). With `<keyName>`: single-key form (reads value from `--value` or stdin). Without `<keyName>`: batch form (reads JSON object from stdin, e.g. `{\"KEY\":\"val\"}`)\n- `wh credential unset <setName> <keyName> [--repo org/repo | --org org]` — Remove a key from a credential set\n- `wh credential audit <setName> [--repo org/repo | --org org]` — View audit log for a credential set\n- `wh credential revoke <setName> [--repo org/repo | --org org] [--reason]` — Revoke a credential set (blocks new binds and stops bound webhook deliveries)\n\n### component — Component management\n- `wh component validate <path>` — Validate package\n- `wh component install <org/name>` — Install a registered component\n- `wh component register <name> --org <org> --manifest <path> [flags]` — Register component identity\n- `wh component unregister <org/name>` — Remove a registered component identity\n- `wh component registry list --org <org>` — List registered components\n- `wh component registry view <org/name>` — View a registered component\n- `wh component registry update <org/name> [flags]` — Update a registered component\n- `wh component list` — List installed components\n- `wh component update <org/name>` — Update installed component\n- `wh component view <org/name>` — Show component details (alias: show)\n- `wh component doctor <org/name>` — Run component health checks\n- `wh component teardown <org/name>` — Pause component subscriptions\n\n### Getting More Info\n- `wh help` — help overview\n- `wh <domain>` — domain verbs\n- `wh <domain> <verb> --help` — verb flags and examples\n- `wh help --format json` — `CliSpecV2` (`schemaVersion: 2`): global/contextual/root tables and command overrides\n\n## Common Workflows\n\n**Explore a repo:**\n```bash\nwh thing list --repo org/repo # see all things in HEAD\nwh thing view Shape/name --repo org/repo # inspect a specific thing\nwh thing history Shape/name --repo org/repo # inspect version history\nwh thing about Shape/name # assertions about thing/shape\n```\n\n**Create an assertion** (most common write):\n```bash\n# --about takes an untyped target wref: Shape or Shape/name.\nwh assertion create --shape MyShape --about TargetShape/target-name \\\n --name my-assertion --data '{\"field_a\":1,\"field_b\":\"value\"}' --repo org/repo\n# Output includes per-operation status; relay failures when present.\n```\n\n**Create via write entrypoint** (alternative, supports batches and streams):\n```bash\nwh commit submit --add my-item --shape MyShape --kind assertion \\\n --about TargetShape/target-name --data '{\"field_a\":1}' --repo org/repo\n```\n\n**Batch write via file** (generate template → edit → submit):\n```bash\nwh shape template Hypothesis Evidence -o ops.json # add --kind assertion --about TargetShape/name for assertions\n# edit ops.json — fill FILL_IN placeholders\nwh commit submit --file ops.json -m \"batch update\" # submit all operations (bare `wh commit` is equivalent)\n# --file format: docs.warmhub.ai/cli-reference/commit-operations\n```\n\n**Bulk write (JSONL stream, >1k ops)** — one chunked stream beats per-record commits (~60× faster):\n```bash\nwh shape template MyShape -o ops.jsonl # one op per line (.jsonl)\nID=\"bulk-$(date +%s)\" # caller id for chunk correlation\nwh commit submit --file ops.jsonl --stream-id \"$ID\" --chunk-size 5000 \\\n --skip-existing --progress -m \"bulk ingest\" --repo org/repo\n# --chunk-size: ops per append chunk (default 1000, max 10000); chunk≈1 is ~60× slower\n# --skip-existing: skips already-written add ops (drops per-row read-before-write)\n# --stream-id identifies the submission but provides no receipt or resume.\n# After an outcome-unknown append, stop writes and reconcile attempted + unsent\n# work from a later verified checkpoint; never blindly replay revise/retract.\n```\n\n**Create collections:**\n```bash\nwh commit submit --type arc --name route --members Location/a,Location/b --repo org/repo\nwh assertion create --shape Supports --name route-support --about Arc/route --data '{\"confidence\":0.8}' --repo org/repo\n```\n\n**Modify data:**\n```bash\nwh thing revise Shape/name --data '{\"x\":5,\"y\":3}' -m \"update\" --repo org/repo\nwh thing retract Shape/old-item -m \"withdrawn\" --reason \"data feed contaminated\" --repo org/repo\n```\n\n**Query and filter:**\n```bash\nwh thing query --shape MyShape # by shape\nwh thing query --kind assertion --about Shape/name # by kind + target\nwh thing history Shape/name --limit 10 # version history\n```\n\n## Built-in Content shape\n\nWarmHub repos expose three well-known content wrefs:\n- `Content/Readme` — stored markdown for humans\n- `Content/Agents` — stored markdown guidance for AI agents\n- `Content/LlmsTxt` — synthesized per-request sitemap (read-only)\n\nFetch via `wh repo content get --kind readme|agents|llms-txt`,\n`client.repo.getReadme/getAgents/getLlmsTxt`, MCP `warmhub_repo_content_get`,\nor raw HTTP `GET /{org}/{repo}/readme.md|agents.md|llms.txt`.\nSee `wh repo describe` → `additionalInformation` for the discovery field.\n\n## Query Discipline\n- Plan the repo, shapes, and wrefs you need before the first query.\n- Gather the needed facts from one repo before switching to another.\n- Do the queries first, then write one complete answer.\n\n## Agent Tips\n- **Always run commands for live data** — this context describes the CLI, not repo contents\n- **Before writing, discover wrefs** — run `wh thing list` or `wh shape list`\n- **Shape field types**: `string`, `number`, `boolean`, `wref`, arrays, optionals, nested objects\n- **Write commands return per-operation results** — relay failures and affected wrefs to the user\n- **Pass data inline** with `--data '{...}'` — do NOT create temp files\n- Add `--json` to any command for machine-readable JSON output\n- **Aliases teach canonical commands** — executing aliases emit a status hint; misleading lifecycle aliases reject and point to `retract`\n- Writes go through `wh commit submit` (or bare `wh commit`) or wrappers (`create`, `revise`, `retract`, `assertion create`). `retract` irreversibly withdraws an identity and creates a history entry.\n- Use `wh doctor` to check environment health\n";
44297
44505
 
44298
44506
  // ../../packages/warmhub-cli/src/domains/prime.ts
44299
44507
  function buildMarkdown(config) {
@@ -45522,6 +45730,9 @@ function renderShapeFieldDiff(out, diff) {
45522
45730
  }
45523
45731
  var retractFlags3 = {
45524
45732
  reason: flag.string({ description: "Reason for retraction (<=500 chars)" }),
45733
+ "expected-version": flag.number({
45734
+ description: "only retract if the target is still at this version (optimistic concurrency)"
45735
+ }),
45525
45736
  message: flag.string({ short: "m", description: "Commit message" }),
45526
45737
  committer: flag.string({
45527
45738
  description: "Committer wref (shape or shaped thing; e.g. Agent/bot-1)"
@@ -45598,16 +45809,18 @@ var handleRevise3 = async (ctx, { flags, args }) => {
45598
45809
  var handleRetract2 = async (ctx, { flags, args }) => {
45599
45810
  const shapeName = args[0];
45600
45811
  if (!shapeName) {
45601
- usageError("Usage: wh shape retract <name>", "wh shape retract Location");
45812
+ usageError("Usage: wh shape retract <name> [--expected-version <n>]", "wh shape retract Location");
45602
45813
  }
45603
45814
  const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
45815
+ const expectedVersion = parsePositiveIntFlag(flags["expected-version"], "--expected-version", "wh shape retract Location --expected-version 3");
45604
45816
  const c = ctx.colors;
45605
45817
  const result = await ctx.client.commit.apply(org, repo, flags.message ?? `retract shape ${shapeName}`, [
45606
45818
  {
45607
45819
  operation: "retract",
45608
45820
  kind: "shape",
45609
45821
  name: shapeName,
45610
- ...flags.reason ? { reason: flags.reason } : {}
45822
+ ...flags.reason ? { reason: flags.reason } : {},
45823
+ ...expectedVersion !== undefined ? { expectedVersion } : {}
45611
45824
  }
45612
45825
  ], { committer: flags.committer });
45613
45826
  assertSingleOpSuccess(result);
@@ -49120,7 +49333,7 @@ function resolveLogLevel(flagLevel, env) {
49120
49333
  // package.json
49121
49334
  var package_default3 = {
49122
49335
  name: "@warmhub/cli",
49123
- version: "0.84.3",
49336
+ version: "0.86.0",
49124
49337
  private: false,
49125
49338
  type: "module",
49126
49339
  description: "The wh CLI for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",
@@ -49737,5 +49950,5 @@ process.exitCode = interceptedExitCode === undefined ? await runPreparedCli(laun
49737
49950
  version: package_default3.version
49738
49951
  }) : interceptedExitCode;
49739
49952
 
49740
- //# debugId=5B259BBB96D29C3364756E2164756E21
49741
- //# warmhub-cli-build-info {"cliVersion":"0.84.3","sdkVersion":"0.82.3"}
49953
+ //# debugId=864ACFE03DA5766664756E2164756E21
49954
+ //# warmhub-cli-build-info {"cliVersion":"0.86.0","sdkVersion":"0.84.0"}