gyojeong 0.1.0 → 0.2.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.
@@ -985,7 +985,6 @@ var string = (params) => {
985
985
  };
986
986
  var integer = /^-?\d+$/;
987
987
  var number = /^-?\d+(?:\.\d+)?$/;
988
- var boolean = /^(?:true|false)$/i;
989
988
  var lowercase = /^[^A-Z]*$/;
990
989
  var uppercase = /^[^a-z]*$/;
991
990
 
@@ -1868,27 +1867,6 @@ var $ZodNumberFormat = /* @__PURE__ */ $constructor("$ZodNumberFormat", (inst, d
1868
1867
  $ZodCheckNumberFormat.init(inst, def);
1869
1868
  $ZodNumber.init(inst, def);
1870
1869
  });
1871
- var $ZodBoolean = /* @__PURE__ */ $constructor("$ZodBoolean", (inst, def) => {
1872
- $ZodType.init(inst, def);
1873
- inst._zod.pattern = boolean;
1874
- inst._zod.parse = (payload, _ctx) => {
1875
- if (def.coerce)
1876
- try {
1877
- payload.value = Boolean(payload.value);
1878
- } catch (_) {
1879
- }
1880
- const input = payload.value;
1881
- if (typeof input === "boolean")
1882
- return payload;
1883
- payload.issues.push({
1884
- expected: "boolean",
1885
- code: "invalid_type",
1886
- input,
1887
- inst
1888
- });
1889
- return payload;
1890
- };
1891
- });
1892
1870
  var $ZodUnknown = /* @__PURE__ */ $constructor("$ZodUnknown", (inst, def) => {
1893
1871
  $ZodType.init(inst, def);
1894
1872
  inst._zod.parse = (payload) => payload;
@@ -2504,28 +2482,6 @@ var $ZodEnum = /* @__PURE__ */ $constructor("$ZodEnum", (inst, def) => {
2504
2482
  return payload;
2505
2483
  };
2506
2484
  });
2507
- var $ZodLiteral = /* @__PURE__ */ $constructor("$ZodLiteral", (inst, def) => {
2508
- $ZodType.init(inst, def);
2509
- if (def.values.length === 0) {
2510
- throw new Error("Cannot create literal schema with no valid values");
2511
- }
2512
- const values = new Set(def.values);
2513
- inst._zod.values = values;
2514
- inst._zod.pattern = new RegExp(`^(${def.values.map((o) => typeof o === "string" ? escapeRegex(o) : o ? escapeRegex(o.toString()) : String(o)).join("|")})$`);
2515
- inst._zod.parse = (payload, _ctx) => {
2516
- const input = payload.value;
2517
- if (values.has(input)) {
2518
- return payload;
2519
- }
2520
- payload.issues.push({
2521
- code: "invalid_value",
2522
- values: def.values,
2523
- input,
2524
- inst
2525
- });
2526
- return payload;
2527
- };
2528
- });
2529
2485
  var $ZodTransform = /* @__PURE__ */ $constructor("$ZodTransform", (inst, def) => {
2530
2486
  $ZodType.init(inst, def);
2531
2487
  inst._zod.optin = "optional";
@@ -3246,13 +3202,6 @@ function _int(Class2, params) {
3246
3202
  });
3247
3203
  }
3248
3204
  // @__NO_SIDE_EFFECTS__
3249
- function _boolean(Class2, params) {
3250
- return new Class2({
3251
- type: "boolean",
3252
- ...normalizeParams(params)
3253
- });
3254
- }
3255
- // @__NO_SIDE_EFFECTS__
3256
3205
  function _unknown(Class2) {
3257
3206
  return new Class2({
3258
3207
  type: "unknown"
@@ -3898,9 +3847,6 @@ var numberProcessor = (schema, ctx, _json, _params) => {
3898
3847
  if (typeof multipleOf === "number")
3899
3848
  json.multipleOf = multipleOf;
3900
3849
  };
3901
- var booleanProcessor = (_schema, _ctx, json, _params) => {
3902
- json.type = "boolean";
3903
- };
3904
3850
  var neverProcessor = (_schema, _ctx, json, _params) => {
3905
3851
  json.not = {};
3906
3852
  };
@@ -3915,46 +3861,6 @@ var enumProcessor = (schema, _ctx, json, _params) => {
3915
3861
  json.type = "string";
3916
3862
  json.enum = values;
3917
3863
  };
3918
- var literalProcessor = (schema, ctx, json, _params) => {
3919
- const def = schema._zod.def;
3920
- const vals = [];
3921
- for (const val of def.values) {
3922
- if (val === void 0) {
3923
- if (ctx.unrepresentable === "throw") {
3924
- throw new Error("Literal `undefined` cannot be represented in JSON Schema");
3925
- } else {
3926
- }
3927
- } else if (typeof val === "bigint") {
3928
- if (ctx.unrepresentable === "throw") {
3929
- throw new Error("BigInt literals cannot be represented in JSON Schema");
3930
- } else {
3931
- vals.push(Number(val));
3932
- }
3933
- } else {
3934
- vals.push(val);
3935
- }
3936
- }
3937
- if (vals.length === 0) {
3938
- } else if (vals.length === 1) {
3939
- const val = vals[0];
3940
- json.type = val === null ? "null" : typeof val;
3941
- if (ctx.target === "draft-04" || ctx.target === "openapi-3.0") {
3942
- json.enum = [val];
3943
- } else {
3944
- json.const = val;
3945
- }
3946
- } else {
3947
- if (vals.every((v) => typeof v === "number"))
3948
- json.type = "number";
3949
- if (vals.every((v) => typeof v === "string"))
3950
- json.type = "string";
3951
- if (vals.every((v) => typeof v === "boolean"))
3952
- json.type = "boolean";
3953
- if (vals.every((v) => v === null))
3954
- json.type = "null";
3955
- json.enum = vals;
3956
- }
3957
- };
3958
3864
  var customProcessor = (_schema, ctx, _json, _params) => {
3959
3865
  if (ctx.unrepresentable === "throw") {
3960
3866
  throw new Error("Custom types cannot be represented in JSON Schema");
@@ -4659,14 +4565,6 @@ var ZodNumberFormat = /* @__PURE__ */ $constructor("ZodNumberFormat", (inst, def
4659
4565
  function int(params) {
4660
4566
  return _int(ZodNumberFormat, params);
4661
4567
  }
4662
- var ZodBoolean = /* @__PURE__ */ $constructor("ZodBoolean", (inst, def) => {
4663
- $ZodBoolean.init(inst, def);
4664
- ZodType.init(inst, def);
4665
- inst._zod.processJSONSchema = (ctx, json, params) => booleanProcessor(inst, ctx, json, params);
4666
- });
4667
- function boolean2(params) {
4668
- return _boolean(ZodBoolean, params);
4669
- }
4670
4568
  var ZodUnknown = /* @__PURE__ */ $constructor("ZodUnknown", (inst, def) => {
4671
4569
  $ZodUnknown.init(inst, def);
4672
4570
  ZodType.init(inst, def);
@@ -4857,27 +4755,6 @@ function _enum(values, params) {
4857
4755
  ...util_exports.normalizeParams(params)
4858
4756
  });
4859
4757
  }
4860
- var ZodLiteral = /* @__PURE__ */ $constructor("ZodLiteral", (inst, def) => {
4861
- $ZodLiteral.init(inst, def);
4862
- ZodType.init(inst, def);
4863
- inst._zod.processJSONSchema = (ctx, json, params) => literalProcessor(inst, ctx, json, params);
4864
- inst.values = new Set(def.values);
4865
- Object.defineProperty(inst, "value", {
4866
- get() {
4867
- if (def.values.length > 1) {
4868
- throw new Error("This schema contains multiple valid literal values. Use `.values` instead.");
4869
- }
4870
- return def.values[0];
4871
- }
4872
- });
4873
- });
4874
- function literal(value, params) {
4875
- return new ZodLiteral({
4876
- type: "literal",
4877
- values: Array.isArray(value) ? value : [value],
4878
- ...util_exports.normalizeParams(params)
4879
- });
4880
- }
4881
4758
  var ZodTransform = /* @__PURE__ */ $constructor("ZodTransform", (inst, def) => {
4882
4759
  $ZodTransform.init(inst, def);
4883
4760
  ZodType.init(inst, def);
@@ -5074,7 +4951,6 @@ var BoundedCorrectionSchema = trimmedNfcBoundedText(4e3, "correction").refine((v
5074
4951
  });
5075
4952
  var IdempotencyKeySchema = string2().trim().min(1).max(128).regex(/^[A-Za-z0-9._:-]+$/);
5076
4953
  var IdentifierSchema = uuid2();
5077
- var DomainSchema = string2().trim().min(1).max(64).regex(/^[a-z][a-z0-9_-]*$/);
5078
4954
  var IsoDateTimeSchema = iso_exports.datetime();
5079
4955
  var ReviewAttachmentFilenameSchema = nfcBoundedText(255, "filename").refine((value) => value.length > 0, { message: "filename must not be empty" }).refine((value) => value !== "." && value !== "..", { message: "filename must be a basename" }).refine((value) => !/[\/\\\0]/u.test(value), { message: "filename must be a basename" });
5080
4956
  var ReviewAttachmentMediaTypeSchema = _enum(["image/png", "image/jpeg", "image/webp"]);
@@ -5174,21 +5050,6 @@ var LanguageTagSchema = string2().transform((value, context) => {
5174
5050
  return NEVER;
5175
5051
  }
5176
5052
  });
5177
- var OperationSchema = _enum(["translate", "rewrite", "post_edit", "review"]);
5178
- var SupportLevelSchema = _enum(["verified", "experimental", "unsupported"]);
5179
- var RouteSchema = object({
5180
- operation: OperationSchema,
5181
- source_language: LanguageTagSchema,
5182
- target_language: LanguageTagSchema,
5183
- domain: DomainSchema,
5184
- support_level: SupportLevelSchema
5185
- }).strict();
5186
- var StructuredErrorSchema = object({
5187
- code: string2().min(1).max(80),
5188
- retryable: boolean2(),
5189
- message: string2().min(1).max(1e3),
5190
- next_action: string2().min(1).max(1e3)
5191
- }).strict();
5192
5053
  var MAX_SURFACE_SELECTOR_CODE_POINTS = 500;
5193
5054
  var MAX_SURFACE_TILES = 32;
5194
5055
  var MAX_SURFACE_TILE_BYTES = 2 * 1024 * 1024;
@@ -5251,20 +5112,6 @@ var SurfaceSnapshotInputSchema = object({
5251
5112
  tiles: array(SurfaceTileInputSchema).min(1).max(MAX_SURFACE_TILES),
5252
5113
  units: array(SurfaceUnitSchema).min(1).max(MAX_SURFACE_UNITS)
5253
5114
  }).strict();
5254
- var SurfaceTileManifestSchema = object({
5255
- index: int().nonnegative(),
5256
- top: int().nonnegative(),
5257
- height: int().positive(),
5258
- media_type: _enum(["image/png", "image/jpeg", "image/webp"]),
5259
- size_bytes: int().positive()
5260
- }).strict();
5261
- var SurfaceSnapshotManifestSchema = object({
5262
- page_width: int().positive(),
5263
- page_height: int().positive(),
5264
- captured_at: IsoDateTimeSchema,
5265
- tiles: array(SurfaceTileManifestSchema).min(1).max(MAX_SURFACE_TILES),
5266
- units: array(SurfaceUnitSchema).min(1).max(MAX_SURFACE_UNITS)
5267
- }).strict();
5268
5115
  var SourceSurfaceSchema = object({
5269
5116
  target_url: url().refine((value) => ["http:", "https:"].includes(new URL(value).protocol), {
5270
5117
  message: "target_url must be an http(s) URL"
@@ -5281,9 +5128,7 @@ var TranslationPublishInputSchema = object({
5281
5128
  run_id: IdentifierSchema,
5282
5129
  source_text: NfcBoundedTextSchema,
5283
5130
  jobs: array(TranslationPublishJobSchema).length(1),
5284
- attachments: array(unknown()).max(MAX_REVIEW_ATTACHMENTS).pipe(array(ReviewAttachmentInputSchema)).optional(),
5285
- upload_consent: literal(true),
5286
- idempotency_key: IdempotencyKeySchema
5131
+ attachments: array(unknown()).max(MAX_REVIEW_ATTACHMENTS).pipe(array(ReviewAttachmentInputSchema)).optional()
5287
5132
  }).strict().superRefine((value, context) => {
5288
5133
  const filenames = /* @__PURE__ */ new Set();
5289
5134
  let totalSize = 0;
@@ -5919,7 +5764,7 @@ function check({ source, draft, mode = detectMode(source), standards = { terms:
5919
5764
  }
5920
5765
 
5921
5766
  // ../../skills/gyojeong/scripts/feedback-resolution-core.mjs
5922
- var MAX_TEXT_CODE_POINTS2 = 2e6;
5767
+ var MAX_TEXT_CODE_POINTS2 = 2 * 1024 * 1024;
5923
5768
  var MAX_FEEDBACK = 1e3;
5924
5769
  var MAX_SELECTION_CODE_POINTS = 4e3;
5925
5770
  var IDENTIFIER = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
@@ -6237,6 +6082,24 @@ import {
6237
6082
  } from "node:fs";
6238
6083
  import { hostname, homedir as homedir2 } from "node:os";
6239
6084
  import path2 from "node:path";
6085
+
6086
+ // ../feedback-core/dist/index.js
6087
+ var createSelectionValidator = (text) => {
6088
+ const normalizedText = text.normalize("NFC");
6089
+ const textCodePoints = Array.from(normalizedText);
6090
+ return (selection) => {
6091
+ if (!Number.isFinite(selection.start) || !Number.isFinite(selection.end) || !Number.isInteger(selection.start) || !Number.isInteger(selection.end) || selection.start < 0 || selection.start >= selection.end || selection.end > textCodePoints.length) {
6092
+ return { ok: false, reason: "stale_selection" };
6093
+ }
6094
+ const selectedSlice = textCodePoints.slice(selection.start, selection.end).join("");
6095
+ if (selectedSlice !== selection.selected_text.normalize("NFC")) {
6096
+ return { ok: false, reason: "stale_selection" };
6097
+ }
6098
+ return { ok: true, normalized_text: normalizedText };
6099
+ };
6100
+ };
6101
+
6102
+ // ../../scripts/review-store.mjs
6240
6103
  var REVIEW_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
6241
6104
  var LOCK_STALE_MS = 3e4;
6242
6105
  var LOCK_WAIT_MS = 1e4;
@@ -6366,10 +6229,9 @@ var createRun = (home, input, { mode, check: check2 } = {}) => {
6366
6229
  id,
6367
6230
  operation: mode === "polish" ? "rewrite" : "translate",
6368
6231
  source_language: mode === "polish" ? "ko" : "en",
6369
- target_languages: [job.target_language],
6370
- domain: "general"
6232
+ target_languages: [job.target_language]
6371
6233
  },
6372
- job: { id: randomUUID(), target_language: job.target_language, support_level: "experimental" },
6234
+ job: { id: randomUUID(), target_language: job.target_language },
6373
6235
  source_text: input.source_text,
6374
6236
  draft_text: job.draft_text,
6375
6237
  attachments,
@@ -6379,13 +6241,19 @@ var createRun = (home, input, { mode, check: check2 } = {}) => {
6379
6241
  };
6380
6242
  const staging = `${dir}.${process.pid}.tmp`;
6381
6243
  ensurePrivateDir(staging);
6382
- (input.attachments ?? []).forEach((attachment, index) => {
6383
- writeFileSync(path2.join(staging, `attachment-${index}.b64`), attachment.content_base64, { mode: 384 });
6384
- });
6385
- writeFileSync(path2.join(staging, "content.json"), serializeReviewContent(content), { mode: 384 });
6386
- writeFileSync(path2.join(staging, "feedback.json"), "[]", { mode: 384 });
6387
- writeFileSync(path2.join(staging, "state.json"), JSON.stringify({ processed_feedback_ids: [] }), { mode: 384 });
6388
- renameSync(staging, dir);
6244
+ try {
6245
+ (input.attachments ?? []).forEach((attachment, index) => {
6246
+ writeFileSync(path2.join(staging, `attachment-${index}.b64`), attachment.content_base64, { mode: 384 });
6247
+ });
6248
+ writeFileSync(path2.join(staging, "content.json"), serializeReviewContent(content), { mode: 384 });
6249
+ writeFileSync(path2.join(staging, "feedback.json"), "[]", { mode: 384 });
6250
+ writeFileSync(path2.join(staging, "state.json"), JSON.stringify({ processed_feedback_ids: [] }), { mode: 384 });
6251
+ writeFileSync(path2.join(staging, "meta.json"), JSON.stringify({ created_at: createdAt }), { mode: 384 });
6252
+ renameSync(staging, dir);
6253
+ } catch (error2) {
6254
+ rmSync(staging, { recursive: true, force: true });
6255
+ throw error2;
6256
+ }
6389
6257
  return { id, job_id: content.job.id, created_at: createdAt };
6390
6258
  };
6391
6259
  var readContent = (home, id) => {
@@ -6407,6 +6275,55 @@ var readRun = (home, id) => {
6407
6275
  pending_feedback_ids: feedback2.map((item) => item.id).filter((fid) => !processed2.has(fid))
6408
6276
  };
6409
6277
  };
6278
+ var selectionChecker = (text) => {
6279
+ let validate;
6280
+ return (selection) => {
6281
+ validate ??= createSelectionValidator(text);
6282
+ try {
6283
+ return validate(selection).ok;
6284
+ } catch {
6285
+ return false;
6286
+ }
6287
+ };
6288
+ };
6289
+ var feedbackDraftVersion = (item, content, fitsCurrent = selectionChecker(content.draft_text)) => {
6290
+ if (item.draft_version !== void 0) return item.draft_version;
6291
+ const current = content.draft_version ?? 0;
6292
+ const legacyRevised = content.draft_version === void 0 && content.revised_at !== void 0;
6293
+ return !legacyRevised || fitsCurrent(item.selection) ? current : -1;
6294
+ };
6295
+ var pendingFeedbackByDraft = (home, id, run) => {
6296
+ const pending = new Set(run.pending_feedback_ids);
6297
+ const fitsCurrent = selectionChecker(run.content.draft_text);
6298
+ const groups = /* @__PURE__ */ new Map();
6299
+ const versionById = /* @__PURE__ */ new Map();
6300
+ for (const item of run.feedback) {
6301
+ if (!pending.has(item.id)) continue;
6302
+ const version2 = feedbackDraftVersion(item, run.content, fitsCurrent);
6303
+ versionById.set(item.id, version2);
6304
+ if (!groups.has(version2)) groups.set(version2, []);
6305
+ groups.get(version2).push(item);
6306
+ }
6307
+ const drafts = /* @__PURE__ */ new Map();
6308
+ const unavailable = [];
6309
+ for (const version2 of groups.keys()) {
6310
+ try {
6311
+ drafts.set(version2, readDraftVersion(home, id, version2, run.content));
6312
+ } catch (error2) {
6313
+ if (!(error2 instanceof StoreProblem)) throw error2;
6314
+ unavailable.push(version2);
6315
+ }
6316
+ }
6317
+ return { groups, drafts, unavailable, versionById };
6318
+ };
6319
+ var readDraftVersion = (home, id, version2, content = readContent(home, id)) => {
6320
+ if (version2 === (content.draft_version ?? 0)) return content.draft_text;
6321
+ const file = path2.join(runDir(home, id), `draft-${version2}.txt`);
6322
+ if (!Number.isInteger(version2) || version2 < 0 || !existsSync2(file)) {
6323
+ throw problem(409, "storage_integrity", "\uD53C\uB4DC\uBC31\uC744 \uB0A8\uAE34 \uCD08\uC548\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", "\uAC80\uC218 \uAE30\uB85D\uC744 \uD655\uC778\uD574 \uC8FC\uC138\uC694.");
6324
+ }
6325
+ return readFileSync2(file, "utf8");
6326
+ };
6410
6327
  var markProcessed = (home, id, feedbackIds) => {
6411
6328
  const dir = runDir(home, id);
6412
6329
  assertRunExists(dir);
@@ -6420,7 +6337,13 @@ var markProcessed = (home, id, feedbackIds) => {
6420
6337
  const state = readJson(path2.join(dir, "state.json"), { processed_feedback_ids: [] });
6421
6338
  const processed2 = [.../* @__PURE__ */ new Set([...state.processed_feedback_ids ?? [], ...feedbackIds])];
6422
6339
  writeAtomic(path2.join(dir, "state.json"), JSON.stringify({ ...state, processed_feedback_ids: processed2 }));
6423
- return { processed: processed2.length, pending: feedback2.filter((item) => !processed2.includes(item.id)).length };
6340
+ const done = new Set(processed2);
6341
+ const needed = new Set(feedback2.filter((item) => !done.has(item.id)).map((item) => item.draft_version));
6342
+ for (const file of readdirSync(dir)) {
6343
+ const match = /^draft-(\d+)\.txt$/u.exec(file);
6344
+ if (match && !needed.has(Number(match[1]))) rmSync(path2.join(dir, file), { force: true });
6345
+ }
6346
+ return { processed: processed2.length, pending: feedback2.filter((item) => !done.has(item.id)).length };
6424
6347
  });
6425
6348
  };
6426
6349
  var reviseDraft = (home, id, draftText, { check: check2 } = {}) => {
@@ -6431,9 +6354,31 @@ var reviseDraft = (home, id, draftText, { check: check2 } = {}) => {
6431
6354
  if (content.draft_text === draftText) {
6432
6355
  throw problem(409, "unchanged_draft", "\uC218\uC815\uBCF8\uC774 \uAE30\uC874 \uCD08\uC548\uACFC \uAC19\uC2B5\uB2C8\uB2E4.", "\uC218\uC815\uD55C \uD30C\uC77C\uC744 \uC9C0\uC815\uD588\uB294\uC9C0 \uD655\uC778\uD574 \uC8FC\uC138\uC694.");
6433
6356
  }
6357
+ if (content.source_surface?.snapshot) {
6358
+ throw problem(409, "surface_review", "\uCEA1\uCC98\uD55C \uD398\uC774\uC9C0 \uAC80\uC218\uB294 \uC218\uC815\uBCF8\uC73C\uB85C \uBC14\uAFC0 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", "\uC218\uC815\uD55C \uD30C\uC77C\uB85C \uC0C8 \uAC80\uC218\uB97C \uAC8C\uC2DC\uD574 \uC8FC\uC138\uC694.");
6359
+ }
6360
+ const version2 = content.draft_version ?? 0;
6434
6361
  const { source_surface: _surface, check: _check2, ...rest } = content;
6435
- const next = { ...rest, draft_text: draftText, ...check2 ? { check: check2 } : {}, revised_at: (/* @__PURE__ */ new Date()).toISOString() };
6436
- writeAtomic(path2.join(dir, "content.json"), serializeReviewContent(next));
6362
+ const next = {
6363
+ ...rest,
6364
+ draft_text: draftText,
6365
+ draft_version: version2 + 1,
6366
+ ...check2 ? { check: check2 } : {},
6367
+ revised_at: (/* @__PURE__ */ new Date()).toISOString()
6368
+ };
6369
+ const serialized = serializeReviewContent(next);
6370
+ const feedback2 = readJson(path2.join(dir, "feedback.json"), []);
6371
+ const unversioned = feedback2.filter((item) => item.draft_version === void 0);
6372
+ if (unversioned.length) {
6373
+ const fitsCurrent = selectionChecker(content.draft_text);
6374
+ for (const item of unversioned) item.draft_version = feedbackDraftVersion(item, content, fitsCurrent);
6375
+ writeAtomic(path2.join(dir, "feedback.json"), JSON.stringify(feedback2, null, 2));
6376
+ }
6377
+ const processed2 = new Set(readJson(path2.join(dir, "state.json"), { processed_feedback_ids: [] }).processed_feedback_ids ?? []);
6378
+ if (feedback2.some((item) => !processed2.has(item.id) && item.draft_version === version2)) {
6379
+ writeAtomic(path2.join(dir, `draft-${version2}.txt`), content.draft_text);
6380
+ }
6381
+ writeAtomic(path2.join(dir, "content.json"), serialized);
6437
6382
  return { id, revised_at: next.revised_at };
6438
6383
  });
6439
6384
  };
@@ -6457,15 +6402,23 @@ var listRuns = (home, now = Date.now()) => {
6457
6402
  if (!existsSync2(runsDir(home))) return [];
6458
6403
  return readdirSync(runsDir(home)).filter((name) => REVIEW_ID.test(name)).flatMap((id) => {
6459
6404
  try {
6460
- const run = readRun(home, id);
6461
- const createdAt = run.content.created_at;
6405
+ const dir = runDir(home, id);
6406
+ let meta2 = null;
6407
+ try {
6408
+ meta2 = readJson(path2.join(dir, "meta.json"), null);
6409
+ } catch {
6410
+ meta2 = null;
6411
+ }
6412
+ const createdAt = (meta2?.created_at ? meta2 : readJson(path2.join(dir, "content.json"), null)).created_at;
6413
+ const feedback2 = readJson(path2.join(dir, "feedback.json"), []);
6414
+ const processed2 = new Set(readJson(path2.join(dir, "state.json"), { processed_feedback_ids: [] }).processed_feedback_ids ?? []);
6462
6415
  return [
6463
6416
  {
6464
6417
  review_id: id,
6465
6418
  created_at: createdAt,
6466
6419
  age_days: Math.floor((now - Date.parse(createdAt)) / 864e5),
6467
- feedback: run.feedback.length,
6468
- pending: run.pending_feedback_ids.length
6420
+ feedback: feedback2.length,
6421
+ pending: feedback2.filter((item) => !processed2.has(item.id)).length
6469
6422
  }
6470
6423
  ];
6471
6424
  } catch {
@@ -6495,12 +6448,13 @@ var parseArgs = (argv) => {
6495
6448
  }
6496
6449
  return options;
6497
6450
  };
6498
- var readText = (file) => {
6451
+ var readRaw = (file, maxBytes = MAX_TEXT_BYTES) => {
6499
6452
  const stat = statSync2(file, { throwIfNoEntry: false });
6500
6453
  if (!stat?.isFile()) throw new UsageError(`not a file: ${file}`);
6501
- if (stat.size > MAX_TEXT_BYTES) throw new UsageError(`file larger than 2 MiB: ${file}`);
6502
- return readFileSync3(file, "utf8").normalize("NFC");
6454
+ if (stat.size > maxBytes) throw new UsageError(`file larger than ${maxBytes / 1024 / 1024} MiB: ${file}`);
6455
+ return readFileSync3(file, "utf8");
6503
6456
  };
6457
+ var readText = (file) => readRaw(file).normalize("NFC");
6504
6458
  var requireOptions = (options, ...keys) => {
6505
6459
  for (const key of keys) if (!options[key]) throw new UsageError(`--${key} is required`);
6506
6460
  };
@@ -6567,10 +6521,15 @@ var ensureServer = async (home) => {
6567
6521
  release();
6568
6522
  }
6569
6523
  };
6570
- var reviewUrls = (port, id) => {
6571
- const local = `http://127.0.0.1:${port}/#review=${id}`;
6572
- const { publicOrigin } = loadIdleConfig();
6573
- return publicOrigin ? { url: local, remote_url: `${publicOrigin}/#review=${id}` } : { url: local };
6524
+ var reviewUrls = (port, id, content, { publicOrigin } = loadIdleConfig()) => {
6525
+ const url3 = `http://127.0.0.1:${port}/#review=${id}`;
6526
+ const surfaceUrl = content.source_surface?.snapshot ? `http://127.0.0.1:${port}/feedback-wrapper#review=${id}` : void 0;
6527
+ return {
6528
+ url: url3,
6529
+ ...surfaceUrl ? { surface_url: surfaceUrl } : {},
6530
+ ...publicOrigin ? { remote_url: `${publicOrigin}/#review=${id}` } : {},
6531
+ ...publicOrigin && surfaceUrl ? { remote_surface_url: `${publicOrigin}/feedback-wrapper#review=${id}` } : {}
6532
+ };
6574
6533
  };
6575
6534
  var attachmentInput = (file) => {
6576
6535
  const mediaType = MEDIA_TYPES[path3.extname(file).toLowerCase()];
@@ -6583,21 +6542,19 @@ var publish = async (home, options) => {
6583
6542
  const draftText = readText(options.draft);
6584
6543
  const mode = options.mode ?? detectMode(sourceText);
6585
6544
  if (mode !== "translate" && mode !== "polish") throw new UsageError("--mode must be translate or polish");
6586
- const surface = options.surface ? SourceSurfaceSchema.parse(JSON.parse(readText(options.surface))) : void 0;
6545
+ const surface = options.surface ? JSON.parse(readRaw(options.surface, MAX_REVIEW_CONTENT_BYTES)) : void 0;
6587
6546
  const input = TranslationPublishInputSchema.parse({
6588
6547
  run_id: randomUUID2(),
6589
6548
  source_text: sourceText,
6590
6549
  jobs: [{ target_language: "ko", draft_text: draftText, ...surface ? { source_surface: surface } : {} }],
6591
- ...options.attach.length ? { attachments: options.attach.map(attachmentInput) } : {},
6592
- upload_consent: true,
6593
- idempotency_key: randomUUID2()
6550
+ ...options.attach.length ? { attachments: options.attach.map(attachmentInput) } : {}
6594
6551
  });
6595
6552
  const report = check({ source: sourceText, draft: draftText, mode, standards: loadStandards() });
6596
6553
  const run = createRun(home, input, { mode, check: report });
6597
6554
  const checkSummary = { result: report.result, exit_code: report.exit_code };
6598
6555
  try {
6599
6556
  const { port } = await ensureServer(home);
6600
- return { review_id: run.id, ...reviewUrls(port, run.id), check: checkSummary };
6557
+ return { review_id: run.id, ...reviewUrls(port, run.id, { source_surface: surface }), check: checkSummary };
6601
6558
  } catch (error2) {
6602
6559
  error2.body = {
6603
6560
  code: "server_unavailable",
@@ -6621,11 +6578,23 @@ var revise = (home, options) => {
6621
6578
  var feedback = (home, options) => {
6622
6579
  requireOptions(options, "review");
6623
6580
  const run = readRun(home, options.review);
6581
+ const current = run.content.draft_version ?? 0;
6582
+ const { drafts, unavailable, versionById } = pendingFeedbackByDraft(home, options.review, run);
6583
+ const earlierDrafts = Object.fromEntries([...drafts].filter(([version2]) => version2 !== current));
6584
+ const fitsCurrent = selectionChecker(run.content.draft_text);
6624
6585
  return {
6625
6586
  review_id: options.review,
6626
6587
  source_text: run.content.source_text,
6627
6588
  draft_text: run.content.draft_text,
6628
- feedback: run.feedback,
6589
+ draft_version: current,
6590
+ ...Object.keys(earlierDrafts).length ? { earlier_drafts: earlierDrafts } : {},
6591
+ // Items on these versions cannot be located; ask the user to leave them again.
6592
+ ...unavailable.length ? { unavailable_draft_versions: unavailable } : {},
6593
+ // Items written without a version carry the one they resolve to, so every item can be matched
6594
+ // against draft_version, earlier_drafts, and unavailable_draft_versions.
6595
+ feedback: run.feedback.map(
6596
+ (item) => item.draft_version === void 0 ? { ...item, draft_version: versionById.get(item.id) ?? feedbackDraftVersion(item, run.content, fitsCurrent) } : item
6597
+ ),
6629
6598
  processed_feedback_ids: run.processed_feedback_ids,
6630
6599
  pending_feedback_ids: run.pending_feedback_ids
6631
6600
  };
@@ -6635,33 +6604,76 @@ var verifyFeedback = (home, options) => {
6635
6604
  const run = readRun(home, options.review);
6636
6605
  const pending = new Set(run.pending_feedback_ids);
6637
6606
  const items = run.feedback.filter((item) => pending.has(item.id));
6638
- if (items.length === 0) return { review_id: options.review, summary: { resolved: 0, unresolved: 0, not_verifiable: 0 }, resolutions: [] };
6639
- const payload = validatePayload({
6640
- schema_version: 1,
6641
- before_text: run.content.draft_text,
6642
- after_text: readText(options.draft),
6643
- resume: {
6644
- run_id: options.review,
6645
- state: "reviewed",
6646
- feedback: items.map((item) => ({
6647
- feedback_id: item.id,
6648
- job_id: item.job_id,
6649
- category: item.category,
6650
- scope: item.scope,
6651
- selection: item.selection,
6652
- has_correction: typeof item.correction === "string",
6653
- ...item.term ? { term: item.term } : {}
6654
- })),
6655
- corrections: items.filter((item) => typeof item.correction === "string").map((item) => ({
6656
- correction_id: item.id,
6657
- feedback_id: item.id,
6658
- category: item.category,
6659
- scope: item.scope,
6660
- correction: item.correction
6661
- }))
6607
+ const summary = { resolved: 0, unresolved: 0, not_verifiable: 0 };
6608
+ if (items.length === 0) return { review_id: options.review, schema_version: 1, summary, resolutions: [] };
6609
+ const afterText = readText(options.draft);
6610
+ const { groups, drafts } = pendingFeedbackByDraft(home, options.review, run);
6611
+ const resolutionById = /* @__PURE__ */ new Map();
6612
+ const notVerifiable = (item, check2) => resolutionById.set(item.id, { feedback_id: item.id, category: item.category, status: "not_verifiable", checks: [check2] });
6613
+ for (const [version2, group] of groups) {
6614
+ const beforeText = drafts.get(version2);
6615
+ if (beforeText === void 0) {
6616
+ for (const item of group) notVerifiable(item, "draft_unavailable");
6617
+ continue;
6662
6618
  }
6663
- });
6664
- return { review_id: options.review, ...verify(payload) };
6619
+ const fits = selectionChecker(beforeText);
6620
+ const fitting = [];
6621
+ for (const item of group) {
6622
+ if (fits(item.selection)) {
6623
+ fitting.push(item);
6624
+ } else {
6625
+ notVerifiable(item, "selection_mismatch");
6626
+ }
6627
+ }
6628
+ if (fitting.length === 0) continue;
6629
+ const payloadFor = (entries) => validatePayload({
6630
+ schema_version: 1,
6631
+ before_text: beforeText,
6632
+ after_text: afterText,
6633
+ resume: {
6634
+ run_id: options.review,
6635
+ state: "reviewed",
6636
+ feedback: entries.map((item) => ({
6637
+ feedback_id: item.id,
6638
+ job_id: item.job_id,
6639
+ category: item.category,
6640
+ scope: item.scope,
6641
+ selection: item.selection,
6642
+ has_correction: typeof item.correction === "string",
6643
+ ...item.term ? { term: item.term } : {}
6644
+ })),
6645
+ corrections: entries.filter((item) => typeof item.correction === "string").map((item) => ({
6646
+ correction_id: item.id,
6647
+ feedback_id: item.id,
6648
+ category: item.category,
6649
+ scope: item.scope,
6650
+ correction: item.correction
6651
+ }))
6652
+ }
6653
+ });
6654
+ const batches = [];
6655
+ const collect = (entries) => {
6656
+ try {
6657
+ batches.push(payloadFor(entries));
6658
+ } catch (error2) {
6659
+ if (!(error2 instanceof ResolutionInputError)) throw error2;
6660
+ if (entries.length === 1) {
6661
+ notVerifiable(entries[0], "invalid_feedback");
6662
+ return;
6663
+ }
6664
+ const middle = Math.ceil(entries.length / 2);
6665
+ collect(entries.slice(0, middle));
6666
+ collect(entries.slice(middle));
6667
+ }
6668
+ };
6669
+ collect(fitting);
6670
+ for (const payload of batches) {
6671
+ for (const resolution of verify(payload).resolutions) resolutionById.set(resolution.feedback_id, resolution);
6672
+ }
6673
+ }
6674
+ const resolutions = items.map((item) => resolutionById.get(item.id));
6675
+ for (const resolution of resolutions) summary[resolution.status] += 1;
6676
+ return { review_id: options.review, schema_version: 1, summary, resolutions };
6665
6677
  };
6666
6678
  var processed = (home, options) => {
6667
6679
  requireOptions(options, "review", "ids");
@@ -6670,9 +6682,9 @@ var processed = (home, options) => {
6670
6682
  };
6671
6683
  var url2 = async (home, options) => {
6672
6684
  requireOptions(options, "review");
6673
- readContent(home, options.review);
6685
+ const content = readContent(home, options.review);
6674
6686
  const { port } = await ensureServer(home);
6675
- return { review_id: options.review, ...reviewUrls(port, options.review) };
6687
+ return { review_id: options.review, ...reviewUrls(port, options.review, content ?? {}) };
6676
6688
  };
6677
6689
  var list = (home) => ({
6678
6690
  stale_after_days: STALE_DAYS,
@@ -6714,5 +6726,6 @@ if (process.argv[1] && path3.resolve(process.argv[1]) === fileURLToPath(import.m
6714
6726
  }
6715
6727
  export {
6716
6728
  ensureServer,
6717
- main
6729
+ main,
6730
+ reviewUrls
6718
6731
  };