@things-factory/figure-service 10.1.47 → 10.1.49

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.
@@ -166,6 +166,18 @@ function v3Wording(violation, say, hasParts) {
166
166
  how: say(key('how'), { parts }, `Fasten ${parts} to a face of the part they rest on, or press "fix here" under placement, so they stay put when the figure is resized.`)
167
167
  };
168
168
  }
169
+ case 'SIZING_NOT_UNIFORM': {
170
+ /* The figure declares it grows by one ratio; these parts keep a size or place of their own (ruling 2026-09-24). */
171
+ const resize = violation.resize;
172
+ const parts = (violation.parts ?? []).map(one => `'${one}'`).join(', ');
173
+ const axes = resize.axes.map(one => one.toUpperCase()).join('·');
174
+ const factor = resize.factor;
175
+ return {
176
+ message: say(key('message'), { parts, axes, factor }, `The figure grows by one ratio, but at ${factor}x on ${axes} ${parts} do not grow with it.`),
177
+ why: say(key('why'), {}, 'A figure that grows by one ratio keeps its proportions at any size. A part with a size or place of its own comes apart from the rest.'),
178
+ how: say(key('how'), { parts }, `Let ${parts} follow the figure's size ("in proportion to size" under size and placement), or remove their own fixed numbers.`)
179
+ };
180
+ }
169
181
  }
170
182
  }
171
183
  /*
@@ -1 +1 @@
1
- {"version":3,"file":"figure-format.js","sourceRoot":"","sources":["../../../server/service/figure/figure-format.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Which format a stored source is written in, and one door per job that reads either.\n *\n * ## Why this exists\n *\n * The cutover (ADR-0087) converts assets to V3 and the `source` column holds one or the other while that\n * happens. Every server path that reads a source — saving, publishing, restoring a version, handing a\n * blueprint to a board — had exactly one implementation, the V2 one, so a V3 asset saved through\n * `saveFigureV3` could not be published, restored or drawn. Branching at each of those four places would put\n * the same question in four voices; it is asked once, here.\n *\n * ## How the format is known\n *\n * The asset says so. A V2 source carries `version: 2`; a V3 asset carries `version: 'figure-v3-asset-1'`.\n * Nothing is guessed from the shape of the object: a source that says neither is refused by name, because a\n * source the server cannot identify is one it must not save.\n *\n * ## What is not here\n *\n * No conversion. This door reads what is stored; turning a V2 asset into a V3 one is `convertV2ToV3`, run\n * deliberately against a recoverable copy (the designer's step 3), never as a side effect of a save.\n */\nimport { compile, compileV3Asset, inspectV3Asset, isPlaceholderType, parseV3Asset, serializeV3Asset, v3ContractChanges, v3CostOf, v3ScoreOf } from '@hatiolab/figure-model'\nimport type { V3Asset, V3ContractChange, V3ContractError, V3Violation } from '@hatiolab/figure-model'\n\nimport type { GateFinding, Inspection } from './figure-gate.js'\nimport { inspectSource } from './figure-gate.js'\nimport { FigureSourceError, parseSource } from './figure-source.js'\nimport type { ParsedSource } from './figure-source.js'\n\nexport type FigureFormat = 'v2' | 'v3'\n\n/** The version each format stamps on its own source. */\nexport const V3_ASSET_VERSION = 'figure-v3-asset-1'\n\n/**\n * Which format this source is written in, read from the `version` the asset carries.\n *\n * @throws {FigureSourceError} when the text is not JSON, or carries no version this server knows.\n */\nexport function formatOf(sourceJson: string): FigureFormat {\n let parsed: unknown\n try {\n parsed = JSON.parse(sourceJson)\n } catch (e) {\n throw new FigureSourceError([\n { code: 'source-not-json', message: `정본이 JSON 이 아니다: ${(e as Error).message}`, at: 'source' }\n ])\n }\n const version = (parsed as { version?: unknown } | null)?.version\n if (version === V3_ASSET_VERSION) return 'v3'\n if (typeof version === 'number') return 'v2'\n throw new FigureSourceError([\n {\n code: 'source-format-unknown',\n message: `정본의 판을 알 수 없다 — V2 는 숫자 version 을, V3 자산은 '${V3_ASSET_VERSION}' 을 적는다`,\n at: 'version'\n }\n ])\n}\n\n/**\n * Validates a source of either format and computes what the catalogue shows.\n *\n * A V3 asset is measured with `v3CostOf`, which charges the same per-shape estimate V2 charges, so the\n * budget columns mean one thing across the two formats. Its `score` is `v3ScoreOf`'s -- the headroom against\n * placing `LIMITS.screenInstances` of it on one screen (chief architect, 2026-09-24), the same number the\n * modeller's maturity tab shows. An asset that draws nothing has no score, not a full one.\n *\n * @throws {FigureSourceError} when the source breaks its own format.\n */\nexport function parseAnySource(sourceJson: string): ParsedSource & { format: FigureFormat } {\n if (formatOf(sourceJson) === 'v2') return { ...parseSource(sourceJson), format: 'v2' }\n\n let asset\n try {\n asset = parseV3Asset(sourceJson)\n compileV3Asset(asset)\n } catch (e) {\n const error = e as V3ContractError & { path?: string }\n throw new FigureSourceError([\n { code: error.code ?? 'v3-asset-invalid', message: error.message ?? String(e), at: error.path }\n ])\n }\n const cost = v3CostOf(asset)\n const score = v3ScoreOf(asset)\n return {\n format: 'v3',\n source: serializeV3Asset(asset),\n derived: { score: (score?.score ?? null) as unknown as number, triangles: cost.triangles, groups: cost.groups },\n violations: []\n }\n}\n\n/**\n * Whether this source may be published, and why not.\n *\n * For V3 the judgement is `inspectV3Asset`: every part inside the volume the asset declares, over the whole\n * state range, and the face it says it is mounted on lying in that plane. A verdict reached by sampling is\n * reported as a finding of its own rather than hidden — an author releasing a figure the gate could only\n * sample should be told so, because nothing proved it holds between the states that were checked.\n */\n/** The request's translator. Always called with params — `t(key, undefined)` loses the locale. */\nexport type Translate = (key: string, options?: Record<string, unknown>) => string\n\n/**\n * A V3 finding in the words of the release panel.\n *\n * The gate says what it measured as a code and values (`part`, `axis`, `mm`, `degrees`) and an English `detail`\n * for logs. The sentence an author reads is made here, where the request's language is known (ADR-0054) — not in\n * figure-model, which knows no locale, and not on the screen, which would then have a second account of the same\n * refusal. The English line is the fallback when a key is missing, so a missing translation reads as English\n * rather than as the key printed raw.\n *\n * The words are the modeller's: 「공간 선언」 for the volume a figure takes up and 「장착면」 for the face it stands on\n * (`figure.text.mounting-face-explained`). The same register as the V2 blockers since 2026-09-19 — a fact, then\n * the way out — because at ADR-0087 step 5 these become the only findings an author sees.\n */\nfunction v3Wording(violation: V3Violation, say: Say, hasParts: boolean): Pick<GateFinding, 'message' | 'why' | 'how'> {\n const part = violation.part ?? ''\n const axis = (violation.axis ?? '').toUpperCase()\n const mm = violation.mm ?? 0\n const degrees = violation.degrees ?? 0\n const key = (field: string) => `figure.gate.${violation.code.toLowerCase().replace(/_/g, '-')}.${field}`\n\n switch (violation.code) {\n case 'PART_OUTSIDE_OCCUPANCY':\n return {\n message: say(key('message'), { part, axis, mm }, `'${part}' goes ${mm} mm past the declared space on ${axis}.`),\n why: say(key('why'), {}, 'The declared space sets the scale a figure is placed at. What lies past it is left out of that scale, so on a board this part lands outside the place given to the figure.'),\n how: say(key('how'), { part, axis, mm }, `Use \"fit to parts\" to declare the space from the parts again, or move '${part}' ${mm} mm inward on ${axis}.`)\n }\n case 'OCCUPANCY_UNDECLARED':\n return violation.axis\n ? {\n message: say(`figure.gate.occupancy-empty-axis.message`, { axis }, `The declared space is empty on ${axis}.`),\n why: say(key('why'), {}, 'Without a declared space the scale a figure is placed at cannot be set.'),\n how: say(key('how'), {}, 'Use \"fit to parts\" to declare the space from the parts as they are now.')\n }\n : {\n message: say(key('message'), {}, 'This figure has not declared the space it takes up.'),\n why: say(key('why'), {}, 'Without a declared space the scale a figure is placed at cannot be set.'),\n /*\n A new figure is refused this way before it has a part, and 「fit to parts」 has nothing to measure then —\n the screen does not offer it. Telling the author to press it sent them looking for a button that was not\n there (:3300, 2026-09-24).\n */\n how: hasParts\n ? say(key('how'), {}, 'Use \"fit to parts\" to declare the space from the parts as they are now.')\n : say(key('how-no-parts'), {}, 'Add a shape first, then use \"fit to parts\" to declare the space from it.')\n }\n case 'PARTS_OFF_PLACEMENT_FACE':\n return {\n message: say(key('message'), { part, mm }, `The mounting face of '${part}' is ${mm} mm off the plane the figure stands on.`),\n why: say(key('why'), {}, 'The figure is declared to stand on that face. If the face does not reach the plane, the figure floats on the board by that much, and whatever is placed on it floats too.'),\n how: say(key('how'), { part, mm }, `Move '${part}' ${mm} mm on Y so the face meets the plane, or choose a different mounting face.`)\n }\n case 'PLACEMENT_FACE_TILTED':\n return {\n message: say(key('message'), { part, degrees }, `The mounting face of '${part}' is tilted ${degrees}° from the plane the figure stands on.`),\n why: say(key('why'), {}, 'A tilted face cannot lie on the plane, so how the figure stands is not settled.'),\n how: say(key('how'), { part }, `Choose a mounting face parallel to the plane, or turn '${part}' upright.`)\n }\n case 'PARTS_SPLIT_ON_RESIZE': {\n /*\n What comes off is every piece but the largest — the figure as the author sees it is the big piece, and the\n small ones are what they will see hanging in the air (V2's sizing-splits, carried to V3 on 2026-09-24).\n */\n const resize = violation.resize!\n const pieces = [...resize.pieces].sort((a, b) => b.length - a.length)\n const parts = pieces.slice(1).flat().map(one => `'${one}'`).join(', ')\n const axes = resize.axes.map(one => one.toUpperCase()).join('·')\n const factor = resize.factor\n return {\n message: say(key('message'), { parts, axes, factor }, `At ${factor}x on ${axes}, ${parts} come away from the rest of the figure.`),\n why: say(key('why'), {}, 'A figure placed at another size shows those parts hanging in the air — to a person, it looks broken.'),\n how: say(key('how'), { parts }, `Fasten ${parts} to a face of the part they rest on, or press \"fix here\" under placement, so they stay put when the figure is resized.`)\n }\n }\n }\n}\n\ntype Say = (key: string, params: Record<string, string | number>, english: string) => string\n\n/*\n The sentence is shown as text, never as HTML, so the values go in as they are. i18next escapes interpolated\n values by default, and the screen then showed `'lamp-in'` as `&#39;lamp-in&#39;`.\n*/\nfunction sayerOf(t?: Translate): Say {\n return (key, params, english) =>\n t ? t(key, { ...params, defaultValue: english, interpolation: { escapeValue: false } }) : english\n}\n\nexport function inspectAnySource(\n sourceJson?: string | null,\n releasedJson?: string | null,\n t?: Translate,\n /** The row's `type` — for V3 the only place the name lives, since the asset carries none. */\n rowType?: string\n): Inspection {\n if (!sourceJson) return inspectSource(sourceJson, releasedJson)\n\n let format: FigureFormat\n try {\n format = formatOf(sourceJson)\n } catch (e) {\n const findings = (e as FigureSourceError).findings ?? [{ code: 'source-unreadable', message: String(e) }]\n return { blocked: true, findings: findings.map(f => ({ ...f, blocking: true })) }\n }\n if (format === 'v2') return inspectSource(sourceJson, releasedJson)\n\n let inspection\n let asset: V3Asset | undefined\n let hasParts = true\n try {\n asset = parseV3Asset(sourceJson)\n inspection = inspectV3Asset(asset)\n const cost = v3CostOf(asset)\n hasParts = cost.groups + cost.hidden > 0\n } catch (e) {\n const error = e as V3ContractError & { path?: string }\n return {\n blocked: true,\n findings: [{ code: error.code ?? 'v3-asset-invalid', message: error.message ?? String(e), at: error.path, blocking: true }]\n }\n }\n\n const say = sayerOf(t)\n const findings: GateFinding[] = inspection.violations.map(violation => ({\n code: violation.code.toLowerCase().replace(/_/g, '-'),\n ...v3Wording(violation, say, hasParts),\n at: violation.part,\n blocking: true\n }))\n /*\n V2 refuses a machine-made name at release (`type-is-placeholder`, from the source's own `type`). A V3 asset has\n no `type`, so that check never ran and a new V3 figure was published as `FIGURE_<uuid>` — seen on :3300,\n 2026-09-24. Publishing freezes the name (ADR-0063 decision 4) and boards call a figure by it, so the same\n refusal stands here, read off the row.\n */\n if (rowType && isPlaceholderType(rowType)) {\n findings.push({\n code: 'type-is-placeholder',\n message: say('figure.gate.type-is-placeholder.message', { type: rowType }, `'${rowType}' is a temporary type given when the figure was first saved.`),\n why: say('figure.gate.type-is-placeholder.why', {}, 'Boards call a figure by its type, and it cannot change once the figure is published.'),\n how: say('figure.gate.type-is-placeholder.how', {}, 'Set the type in the type field, then publish.'),\n blocking: true\n })\n }\n if (inspection.coverage.method === 'sampled') {\n const states = inspection.coverage.states\n findings.push({\n code: 'state-range-sampled',\n message: say('figure.gate.state-range-sampled.message', { states }, `The whole range of motion could not be proven, so ${states} poses were checked.`),\n why: say('figure.gate.state-range-sampled.why', {}, 'Between the poses that were checked, a part may still go past the declared space.'),\n how: say('figure.gate.state-range-sampled.how', {}, 'Re-author the motion that could not be proven, and the whole range can be proven.'),\n /*\n Which motions stopped the proof, by the name a person gave them: the kernel reports each operation it could\n not prove (`j2-shoulder.chain: compose@1 turns …`), and the screen showed that English list as it was. The\n name before the first dot is the joint or part; the operations stay in the logs.\n */\n at: [...new Set((inspection.coverage.unprovable ?? []).map(entry => entry.split(':')[0]!.split('.')[0]!.trim()).filter(Boolean))].join(' · ') || undefined,\n blocking: false\n })\n }\n if (releasedJson) findings.push(...v3ContractFindings(asset!, releasedJson, say))\n return { blocked: findings.some(finding => finding.blocking), findings }\n}\n\n/**\n * The last release against this one — what the drawings that already hold this type lean on (ADR-0063 decision 3).\n *\n * figure-model names the changes and whether each blocks (`v3ContractChanges`); the sentence is made here. When\n * the last release cannot be compared — it was a V2 source, which is what every converted figure's last release\n * is, or it no longer parses — the gate says so rather than passing in silence: a release that was not checked\n * against the last one is a different fact from one that was checked and found safe.\n */\nfunction v3ContractFindings(asset: V3Asset, releasedJson: string, say: Say): GateFinding[] {\n let before: V3Asset\n try {\n if (formatOf(releasedJson) === 'v2') {\n return [\n {\n code: 'released-unreadable',\n message: say('figure.gate.v3-released-was-v2.message', {}, 'The last release is a V2 figure, so this release was not compared with it.'),\n why: say('figure.gate.v3-released-not-compared.why', {}, 'Whether this release breaks the instances already placed has not been checked.'),\n blocking: false\n }\n ]\n }\n before = parseV3Asset(releasedJson)\n } catch (e) {\n return [\n {\n code: 'released-unreadable',\n message: say('figure.gate.v3-released-not-compared.message', { reason: (e as Error).message }, `The last release could not be read, so this release was not compared with it: ${(e as Error).message}`),\n why: say('figure.gate.v3-released-not-compared.why', {}, 'Whether this release breaks the instances already placed has not been checked.'),\n blocking: false\n }\n ]\n }\n return v3ContractChanges(before, asset).map(change => ({\n code: change.code,\n ...v3ContractWording(change, say),\n at: change.subject,\n blocking: change.blocking\n }))\n}\n\n/** One contract change in the words of the release panel. The English line is the fallback for a missing key. */\nfunction v3ContractWording(change: V3ContractChange, say: Say): Pick<GateFinding, 'message' | 'why' | 'how'> {\n const v = change.values\n const key = (field: string) => `figure.gate.contract.${change.code}.${field}`\n const gone = {\n how: say('figure.gate.contract.gone.how', {}, 'Put it back as it was, or publish this as a new figure type so the placed ones keep the old one.')\n }\n switch (change.code) {\n case 'placement-changed':\n return {\n message: say(key('message'), v, `Placement changed from ${v.before} to ${v.after}.`),\n why: say(key('why'), {}, 'Placement sets the height of every placed instance. They all move with it.'),\n ...gone\n }\n case 'size-inputs-gone':\n return {\n message: say(key('message'), v, 'The figure no longer takes its size from the place it is given.'),\n why: say(key('why'), {}, 'Placed instances were sized on the board. After this release they all draw at the authored size.'),\n ...gone\n }\n case 'size-inputs-added':\n return {\n message: say(key('message'), v, 'The figure now takes its size from the place it is given.'),\n why: say(key('why'), {}, 'Placed instances now follow their own size on the board.')\n }\n case 'size-range-narrowed':\n return {\n message: say(key('message'), { ...v, input: change.subject ?? '' }, `The size range of ${change.subject} narrowed from ${v.before} to ${v.after}.`),\n why: say(key('why'), {}, 'An instance placed at a size outside the new range will not draw. Which sizes are placed is not recorded.')\n }\n case 'authored-size-changed':\n return {\n message: say(key('message'), v, `The authored size changed from ${v.before} to ${v.after}.`),\n why: say(key('why'), {}, 'Placed instances keep their own size. An instance with no depth of its own takes its depth from this.')\n }\n case 'state-input-gone':\n return {\n message: say(key('message'), v, `The control '${v.id}' is gone.`),\n why: say(key('why'), {}, 'Placed instances and connectors give values under this name. After this release those values do nothing, and nothing on screen says why.'),\n ...gone\n }\n case 'state-unit-changed':\n return {\n message: say(key('message'), v, `The unit of the control '${v.id}' changed from ${v.before} to ${v.after}.`),\n why: say(key('why'), {}, 'Stored values keep their number. With another unit the same number means something else.'),\n ...gone\n }\n case 'state-range-narrowed':\n return {\n message: say(key('message'), v, `The range of the control '${v.id}' narrowed from ${v.before} to ${v.after}.`),\n why: say(key('why'), {}, 'A value outside the new range may already be stored on a placed instance.')\n }\n case 'state-input-added':\n return {\n message: say(key('message'), v, `The control '${v.id}' is new.`),\n why: say(key('why'), {}, 'Placed instances that give no value stand at its default.')\n }\n case 'clip-gone':\n return {\n message: say(key('message'), v, `The clip '${v.clip}' is gone.`),\n why: say(key('why'), {}, 'Placed instances play clips by name. After this release that one plays nothing.'),\n ...gone\n }\n case 'clip-added':\n return {\n message: say(key('message'), v, `The clip '${v.clip}' is new.`),\n why: say(key('why'), {}, 'It stays still until an instance plays it.')\n }\n case 'binding-gone':\n return {\n message: say(key('message'), v, `The place '${v.binding}' is gone.`),\n why: say(key('why'), {}, 'What sits in it on a placed instance loses its place.'),\n ...gone\n }\n case 'binding-role-changed':\n return {\n message: say(key('message'), v, `The place '${v.binding}' changed from ${v.before} to ${v.after}.`),\n why: say(key('why'), {}, 'What sits in it was placed for the old role.'),\n ...gone\n }\n case 'binding-capacity-reduced':\n return {\n message: say(key('message'), v, `The place '${v.binding}' holds ${v.after} instead of ${v.before}.`),\n why: say(key('why'), {}, 'What sits in the places that went has nowhere to go.'),\n ...gone\n }\n case 'binding-accepts-narrowed':\n return {\n message: say(key('message'), v, `The place '${v.binding}' no longer accepts ${v.types}.`),\n why: say(key('why'), {}, 'What already sits there stays. The next one of that type is refused.')\n }\n case 'binding-added':\n return {\n message: say(key('message'), v, `The place '${v.binding}' is new.`),\n why: say(key('why'), {}, 'Placed instances do not know it yet and stand as they are.')\n }\n case 'capability-gone':\n return {\n message: say(key('message'), v, `The capability '${v.name}' is gone.`),\n why: say(key('why'), {}, 'Scenes attach behaviour by this name. Placed instances lose what it did.'),\n ...gone\n }\n case 'capability-added':\n return {\n message: say(key('message'), v, `The capability '${v.name}' is new.`),\n why: say(key('why'), {}, 'It adds what the figure can do. Placed instances stand as they are.')\n }\n }\n}\n\n/**\n * What a consumer is handed for a released figure.\n *\n * V2 hands over a compiled blueprint; V3 hands over the asset itself, because the thing that draws a V3 asset\n * — `registerFigureV3` in things-scene — takes the asset and compiles it with the kernel. The format travels\n * with it so the consumer calls the right registration rather than sniffing the payload a second time.\n */\nexport function consumablePayloadOf(sourceJson: string): { format: FigureFormat; blueprint?: unknown; asset?: unknown } {\n if (formatOf(sourceJson) === 'v2') return { format: 'v2', blueprint: compile(JSON.parse(sourceJson)) }\n const asset = parseV3Asset(sourceJson)\n compileV3Asset(asset)\n return { format: 'v3', asset }\n}\n"]}
1
+ {"version":3,"file":"figure-format.js","sourceRoot":"","sources":["../../../server/service/figure/figure-format.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Which format a stored source is written in, and one door per job that reads either.\n *\n * ## Why this exists\n *\n * The cutover (ADR-0087) converts assets to V3 and the `source` column holds one or the other while that\n * happens. Every server path that reads a source — saving, publishing, restoring a version, handing a\n * blueprint to a board — had exactly one implementation, the V2 one, so a V3 asset saved through\n * `saveFigureV3` could not be published, restored or drawn. Branching at each of those four places would put\n * the same question in four voices; it is asked once, here.\n *\n * ## How the format is known\n *\n * The asset says so. A V2 source carries `version: 2`; a V3 asset carries `version: 'figure-v3-asset-1'`.\n * Nothing is guessed from the shape of the object: a source that says neither is refused by name, because a\n * source the server cannot identify is one it must not save.\n *\n * ## What is not here\n *\n * No conversion. This door reads what is stored; turning a V2 asset into a V3 one is `convertV2ToV3`, run\n * deliberately against a recoverable copy (the designer's step 3), never as a side effect of a save.\n */\nimport { compile, compileV3Asset, inspectV3Asset, isPlaceholderType, parseV3Asset, serializeV3Asset, v3ContractChanges, v3CostOf, v3ScoreOf } from '@hatiolab/figure-model'\nimport type { V3Asset, V3ContractChange, V3ContractError, V3Violation } from '@hatiolab/figure-model'\n\nimport type { GateFinding, Inspection } from './figure-gate.js'\nimport { inspectSource } from './figure-gate.js'\nimport { FigureSourceError, parseSource } from './figure-source.js'\nimport type { ParsedSource } from './figure-source.js'\n\nexport type FigureFormat = 'v2' | 'v3'\n\n/** The version each format stamps on its own source. */\nexport const V3_ASSET_VERSION = 'figure-v3-asset-1'\n\n/**\n * Which format this source is written in, read from the `version` the asset carries.\n *\n * @throws {FigureSourceError} when the text is not JSON, or carries no version this server knows.\n */\nexport function formatOf(sourceJson: string): FigureFormat {\n let parsed: unknown\n try {\n parsed = JSON.parse(sourceJson)\n } catch (e) {\n throw new FigureSourceError([\n { code: 'source-not-json', message: `정본이 JSON 이 아니다: ${(e as Error).message}`, at: 'source' }\n ])\n }\n const version = (parsed as { version?: unknown } | null)?.version\n if (version === V3_ASSET_VERSION) return 'v3'\n if (typeof version === 'number') return 'v2'\n throw new FigureSourceError([\n {\n code: 'source-format-unknown',\n message: `정본의 판을 알 수 없다 — V2 는 숫자 version 을, V3 자산은 '${V3_ASSET_VERSION}' 을 적는다`,\n at: 'version'\n }\n ])\n}\n\n/**\n * Validates a source of either format and computes what the catalogue shows.\n *\n * A V3 asset is measured with `v3CostOf`, which charges the same per-shape estimate V2 charges, so the\n * budget columns mean one thing across the two formats. Its `score` is `v3ScoreOf`'s -- the headroom against\n * placing `LIMITS.screenInstances` of it on one screen (chief architect, 2026-09-24), the same number the\n * modeller's maturity tab shows. An asset that draws nothing has no score, not a full one.\n *\n * @throws {FigureSourceError} when the source breaks its own format.\n */\nexport function parseAnySource(sourceJson: string): ParsedSource & { format: FigureFormat } {\n if (formatOf(sourceJson) === 'v2') return { ...parseSource(sourceJson), format: 'v2' }\n\n let asset\n try {\n asset = parseV3Asset(sourceJson)\n compileV3Asset(asset)\n } catch (e) {\n const error = e as V3ContractError & { path?: string }\n throw new FigureSourceError([\n { code: error.code ?? 'v3-asset-invalid', message: error.message ?? String(e), at: error.path }\n ])\n }\n const cost = v3CostOf(asset)\n const score = v3ScoreOf(asset)\n return {\n format: 'v3',\n source: serializeV3Asset(asset),\n derived: { score: (score?.score ?? null) as unknown as number, triangles: cost.triangles, groups: cost.groups },\n violations: []\n }\n}\n\n/**\n * Whether this source may be published, and why not.\n *\n * For V3 the judgement is `inspectV3Asset`: every part inside the volume the asset declares, over the whole\n * state range, and the face it says it is mounted on lying in that plane. A verdict reached by sampling is\n * reported as a finding of its own rather than hidden — an author releasing a figure the gate could only\n * sample should be told so, because nothing proved it holds between the states that were checked.\n */\n/** The request's translator. Always called with params — `t(key, undefined)` loses the locale. */\nexport type Translate = (key: string, options?: Record<string, unknown>) => string\n\n/**\n * A V3 finding in the words of the release panel.\n *\n * The gate says what it measured as a code and values (`part`, `axis`, `mm`, `degrees`) and an English `detail`\n * for logs. The sentence an author reads is made here, where the request's language is known (ADR-0054) — not in\n * figure-model, which knows no locale, and not on the screen, which would then have a second account of the same\n * refusal. The English line is the fallback when a key is missing, so a missing translation reads as English\n * rather than as the key printed raw.\n *\n * The words are the modeller's: 「공간 선언」 for the volume a figure takes up and 「장착면」 for the face it stands on\n * (`figure.text.mounting-face-explained`). The same register as the V2 blockers since 2026-09-19 — a fact, then\n * the way out — because at ADR-0087 step 5 these become the only findings an author sees.\n */\nfunction v3Wording(violation: V3Violation, say: Say, hasParts: boolean): Pick<GateFinding, 'message' | 'why' | 'how'> {\n const part = violation.part ?? ''\n const axis = (violation.axis ?? '').toUpperCase()\n const mm = violation.mm ?? 0\n const degrees = violation.degrees ?? 0\n const key = (field: string) => `figure.gate.${violation.code.toLowerCase().replace(/_/g, '-')}.${field}`\n\n switch (violation.code) {\n case 'PART_OUTSIDE_OCCUPANCY':\n return {\n message: say(key('message'), { part, axis, mm }, `'${part}' goes ${mm} mm past the declared space on ${axis}.`),\n why: say(key('why'), {}, 'The declared space sets the scale a figure is placed at. What lies past it is left out of that scale, so on a board this part lands outside the place given to the figure.'),\n how: say(key('how'), { part, axis, mm }, `Use \"fit to parts\" to declare the space from the parts again, or move '${part}' ${mm} mm inward on ${axis}.`)\n }\n case 'OCCUPANCY_UNDECLARED':\n return violation.axis\n ? {\n message: say(`figure.gate.occupancy-empty-axis.message`, { axis }, `The declared space is empty on ${axis}.`),\n why: say(key('why'), {}, 'Without a declared space the scale a figure is placed at cannot be set.'),\n how: say(key('how'), {}, 'Use \"fit to parts\" to declare the space from the parts as they are now.')\n }\n : {\n message: say(key('message'), {}, 'This figure has not declared the space it takes up.'),\n why: say(key('why'), {}, 'Without a declared space the scale a figure is placed at cannot be set.'),\n /*\n A new figure is refused this way before it has a part, and 「fit to parts」 has nothing to measure then —\n the screen does not offer it. Telling the author to press it sent them looking for a button that was not\n there (:3300, 2026-09-24).\n */\n how: hasParts\n ? say(key('how'), {}, 'Use \"fit to parts\" to declare the space from the parts as they are now.')\n : say(key('how-no-parts'), {}, 'Add a shape first, then use \"fit to parts\" to declare the space from it.')\n }\n case 'PARTS_OFF_PLACEMENT_FACE':\n return {\n message: say(key('message'), { part, mm }, `The mounting face of '${part}' is ${mm} mm off the plane the figure stands on.`),\n why: say(key('why'), {}, 'The figure is declared to stand on that face. If the face does not reach the plane, the figure floats on the board by that much, and whatever is placed on it floats too.'),\n how: say(key('how'), { part, mm }, `Move '${part}' ${mm} mm on Y so the face meets the plane, or choose a different mounting face.`)\n }\n case 'PLACEMENT_FACE_TILTED':\n return {\n message: say(key('message'), { part, degrees }, `The mounting face of '${part}' is tilted ${degrees}° from the plane the figure stands on.`),\n why: say(key('why'), {}, 'A tilted face cannot lie on the plane, so how the figure stands is not settled.'),\n how: say(key('how'), { part }, `Choose a mounting face parallel to the plane, or turn '${part}' upright.`)\n }\n case 'PARTS_SPLIT_ON_RESIZE': {\n /*\n What comes off is every piece but the largest — the figure as the author sees it is the big piece, and the\n small ones are what they will see hanging in the air (V2's sizing-splits, carried to V3 on 2026-09-24).\n */\n const resize = violation.resize!\n const pieces = [...resize.pieces].sort((a, b) => b.length - a.length)\n const parts = pieces.slice(1).flat().map(one => `'${one}'`).join(', ')\n const axes = resize.axes.map(one => one.toUpperCase()).join('·')\n const factor = resize.factor\n return {\n message: say(key('message'), { parts, axes, factor }, `At ${factor}x on ${axes}, ${parts} come away from the rest of the figure.`),\n why: say(key('why'), {}, 'A figure placed at another size shows those parts hanging in the air — to a person, it looks broken.'),\n how: say(key('how'), { parts }, `Fasten ${parts} to a face of the part they rest on, or press \"fix here\" under placement, so they stay put when the figure is resized.`)\n }\n }\n case 'SIZING_NOT_UNIFORM': {\n /* The figure declares it grows by one ratio; these parts keep a size or place of their own (ruling 2026-09-24). */\n const resize = violation.resize!\n const parts = (violation.parts ?? []).map(one => `'${one}'`).join(', ')\n const axes = resize.axes.map(one => one.toUpperCase()).join('·')\n const factor = resize.factor\n return {\n message: say(key('message'), { parts, axes, factor }, `The figure grows by one ratio, but at ${factor}x on ${axes} ${parts} do not grow with it.`),\n why: say(key('why'), {}, 'A figure that grows by one ratio keeps its proportions at any size. A part with a size or place of its own comes apart from the rest.'),\n how: say(key('how'), { parts }, `Let ${parts} follow the figure's size (\"in proportion to size\" under size and placement), or remove their own fixed numbers.`)\n }\n }\n }\n}\n\ntype Say = (key: string, params: Record<string, string | number>, english: string) => string\n\n/*\n The sentence is shown as text, never as HTML, so the values go in as they are. i18next escapes interpolated\n values by default, and the screen then showed `'lamp-in'` as `&#39;lamp-in&#39;`.\n*/\nfunction sayerOf(t?: Translate): Say {\n return (key, params, english) =>\n t ? t(key, { ...params, defaultValue: english, interpolation: { escapeValue: false } }) : english\n}\n\nexport function inspectAnySource(\n sourceJson?: string | null,\n releasedJson?: string | null,\n t?: Translate,\n /** The row's `type` — for V3 the only place the name lives, since the asset carries none. */\n rowType?: string\n): Inspection {\n if (!sourceJson) return inspectSource(sourceJson, releasedJson)\n\n let format: FigureFormat\n try {\n format = formatOf(sourceJson)\n } catch (e) {\n const findings = (e as FigureSourceError).findings ?? [{ code: 'source-unreadable', message: String(e) }]\n return { blocked: true, findings: findings.map(f => ({ ...f, blocking: true })) }\n }\n if (format === 'v2') return inspectSource(sourceJson, releasedJson)\n\n let inspection\n let asset: V3Asset | undefined\n let hasParts = true\n try {\n asset = parseV3Asset(sourceJson)\n inspection = inspectV3Asset(asset)\n const cost = v3CostOf(asset)\n hasParts = cost.groups + cost.hidden > 0\n } catch (e) {\n const error = e as V3ContractError & { path?: string }\n return {\n blocked: true,\n findings: [{ code: error.code ?? 'v3-asset-invalid', message: error.message ?? String(e), at: error.path, blocking: true }]\n }\n }\n\n const say = sayerOf(t)\n const findings: GateFinding[] = inspection.violations.map(violation => ({\n code: violation.code.toLowerCase().replace(/_/g, '-'),\n ...v3Wording(violation, say, hasParts),\n at: violation.part,\n blocking: true\n }))\n /*\n V2 refuses a machine-made name at release (`type-is-placeholder`, from the source's own `type`). A V3 asset has\n no `type`, so that check never ran and a new V3 figure was published as `FIGURE_<uuid>` — seen on :3300,\n 2026-09-24. Publishing freezes the name (ADR-0063 decision 4) and boards call a figure by it, so the same\n refusal stands here, read off the row.\n */\n if (rowType && isPlaceholderType(rowType)) {\n findings.push({\n code: 'type-is-placeholder',\n message: say('figure.gate.type-is-placeholder.message', { type: rowType }, `'${rowType}' is a temporary type given when the figure was first saved.`),\n why: say('figure.gate.type-is-placeholder.why', {}, 'Boards call a figure by its type, and it cannot change once the figure is published.'),\n how: say('figure.gate.type-is-placeholder.how', {}, 'Set the type in the type field, then publish.'),\n blocking: true\n })\n }\n if (inspection.coverage.method === 'sampled') {\n const states = inspection.coverage.states\n findings.push({\n code: 'state-range-sampled',\n message: say('figure.gate.state-range-sampled.message', { states }, `The whole range of motion could not be proven, so ${states} poses were checked.`),\n why: say('figure.gate.state-range-sampled.why', {}, 'Between the poses that were checked, a part may still go past the declared space.'),\n how: say('figure.gate.state-range-sampled.how', {}, 'Re-author the motion that could not be proven, and the whole range can be proven.'),\n /*\n Which motions stopped the proof, by the name a person gave them: the kernel reports each operation it could\n not prove (`j2-shoulder.chain: compose@1 turns …`), and the screen showed that English list as it was. The\n name before the first dot is the joint or part; the operations stay in the logs.\n */\n at: [...new Set((inspection.coverage.unprovable ?? []).map(entry => entry.split(':')[0]!.split('.')[0]!.trim()).filter(Boolean))].join(' · ') || undefined,\n blocking: false\n })\n }\n if (releasedJson) findings.push(...v3ContractFindings(asset!, releasedJson, say))\n return { blocked: findings.some(finding => finding.blocking), findings }\n}\n\n/**\n * The last release against this one — what the drawings that already hold this type lean on (ADR-0063 decision 3).\n *\n * figure-model names the changes and whether each blocks (`v3ContractChanges`); the sentence is made here. When\n * the last release cannot be compared — it was a V2 source, which is what every converted figure's last release\n * is, or it no longer parses — the gate says so rather than passing in silence: a release that was not checked\n * against the last one is a different fact from one that was checked and found safe.\n */\nfunction v3ContractFindings(asset: V3Asset, releasedJson: string, say: Say): GateFinding[] {\n let before: V3Asset\n try {\n if (formatOf(releasedJson) === 'v2') {\n return [\n {\n code: 'released-unreadable',\n message: say('figure.gate.v3-released-was-v2.message', {}, 'The last release is a V2 figure, so this release was not compared with it.'),\n why: say('figure.gate.v3-released-not-compared.why', {}, 'Whether this release breaks the instances already placed has not been checked.'),\n blocking: false\n }\n ]\n }\n before = parseV3Asset(releasedJson)\n } catch (e) {\n return [\n {\n code: 'released-unreadable',\n message: say('figure.gate.v3-released-not-compared.message', { reason: (e as Error).message }, `The last release could not be read, so this release was not compared with it: ${(e as Error).message}`),\n why: say('figure.gate.v3-released-not-compared.why', {}, 'Whether this release breaks the instances already placed has not been checked.'),\n blocking: false\n }\n ]\n }\n return v3ContractChanges(before, asset).map(change => ({\n code: change.code,\n ...v3ContractWording(change, say),\n at: change.subject,\n blocking: change.blocking\n }))\n}\n\n/** One contract change in the words of the release panel. The English line is the fallback for a missing key. */\nfunction v3ContractWording(change: V3ContractChange, say: Say): Pick<GateFinding, 'message' | 'why' | 'how'> {\n const v = change.values\n const key = (field: string) => `figure.gate.contract.${change.code}.${field}`\n const gone = {\n how: say('figure.gate.contract.gone.how', {}, 'Put it back as it was, or publish this as a new figure type so the placed ones keep the old one.')\n }\n switch (change.code) {\n case 'placement-changed':\n return {\n message: say(key('message'), v, `Placement changed from ${v.before} to ${v.after}.`),\n why: say(key('why'), {}, 'Placement sets the height of every placed instance. They all move with it.'),\n ...gone\n }\n case 'size-inputs-gone':\n return {\n message: say(key('message'), v, 'The figure no longer takes its size from the place it is given.'),\n why: say(key('why'), {}, 'Placed instances were sized on the board. After this release they all draw at the authored size.'),\n ...gone\n }\n case 'size-inputs-added':\n return {\n message: say(key('message'), v, 'The figure now takes its size from the place it is given.'),\n why: say(key('why'), {}, 'Placed instances now follow their own size on the board.')\n }\n case 'size-range-narrowed':\n return {\n message: say(key('message'), { ...v, input: change.subject ?? '' }, `The size range of ${change.subject} narrowed from ${v.before} to ${v.after}.`),\n why: say(key('why'), {}, 'An instance placed at a size outside the new range will not draw. Which sizes are placed is not recorded.')\n }\n case 'authored-size-changed':\n return {\n message: say(key('message'), v, `The authored size changed from ${v.before} to ${v.after}.`),\n why: say(key('why'), {}, 'Placed instances keep their own size. An instance with no depth of its own takes its depth from this.')\n }\n case 'state-input-gone':\n return {\n message: say(key('message'), v, `The control '${v.id}' is gone.`),\n why: say(key('why'), {}, 'Placed instances and connectors give values under this name. After this release those values do nothing, and nothing on screen says why.'),\n ...gone\n }\n case 'state-unit-changed':\n return {\n message: say(key('message'), v, `The unit of the control '${v.id}' changed from ${v.before} to ${v.after}.`),\n why: say(key('why'), {}, 'Stored values keep their number. With another unit the same number means something else.'),\n ...gone\n }\n case 'state-range-narrowed':\n return {\n message: say(key('message'), v, `The range of the control '${v.id}' narrowed from ${v.before} to ${v.after}.`),\n why: say(key('why'), {}, 'A value outside the new range may already be stored on a placed instance.')\n }\n case 'state-input-added':\n return {\n message: say(key('message'), v, `The control '${v.id}' is new.`),\n why: say(key('why'), {}, 'Placed instances that give no value stand at its default.')\n }\n case 'clip-gone':\n return {\n message: say(key('message'), v, `The clip '${v.clip}' is gone.`),\n why: say(key('why'), {}, 'Placed instances play clips by name. After this release that one plays nothing.'),\n ...gone\n }\n case 'clip-added':\n return {\n message: say(key('message'), v, `The clip '${v.clip}' is new.`),\n why: say(key('why'), {}, 'It stays still until an instance plays it.')\n }\n case 'binding-gone':\n return {\n message: say(key('message'), v, `The place '${v.binding}' is gone.`),\n why: say(key('why'), {}, 'What sits in it on a placed instance loses its place.'),\n ...gone\n }\n case 'binding-role-changed':\n return {\n message: say(key('message'), v, `The place '${v.binding}' changed from ${v.before} to ${v.after}.`),\n why: say(key('why'), {}, 'What sits in it was placed for the old role.'),\n ...gone\n }\n case 'binding-capacity-reduced':\n return {\n message: say(key('message'), v, `The place '${v.binding}' holds ${v.after} instead of ${v.before}.`),\n why: say(key('why'), {}, 'What sits in the places that went has nowhere to go.'),\n ...gone\n }\n case 'binding-accepts-narrowed':\n return {\n message: say(key('message'), v, `The place '${v.binding}' no longer accepts ${v.types}.`),\n why: say(key('why'), {}, 'What already sits there stays. The next one of that type is refused.')\n }\n case 'binding-added':\n return {\n message: say(key('message'), v, `The place '${v.binding}' is new.`),\n why: say(key('why'), {}, 'Placed instances do not know it yet and stand as they are.')\n }\n case 'capability-gone':\n return {\n message: say(key('message'), v, `The capability '${v.name}' is gone.`),\n why: say(key('why'), {}, 'Scenes attach behaviour by this name. Placed instances lose what it did.'),\n ...gone\n }\n case 'capability-added':\n return {\n message: say(key('message'), v, `The capability '${v.name}' is new.`),\n why: say(key('why'), {}, 'It adds what the figure can do. Placed instances stand as they are.')\n }\n }\n}\n\n/**\n * What a consumer is handed for a released figure.\n *\n * V2 hands over a compiled blueprint; V3 hands over the asset itself, because the thing that draws a V3 asset\n * — `registerFigureV3` in things-scene — takes the asset and compiles it with the kernel. The format travels\n * with it so the consumer calls the right registration rather than sniffing the payload a second time.\n */\nexport function consumablePayloadOf(sourceJson: string): { format: FigureFormat; blueprint?: unknown; asset?: unknown } {\n if (formatOf(sourceJson) === 'v2') return { format: 'v2', blueprint: compile(JSON.parse(sourceJson)) }\n const asset = parseV3Asset(sourceJson)\n compileV3Asset(asset)\n return { format: 'v3', asset }\n}\n"]}
@@ -64,6 +64,9 @@ function getDeps() {
64
64
  complaint. The tool's own contract is unchanged: it reads, it never saves (ADR-0087).
65
65
  */
66
66
  inspectSource: format.inspectAnySource,
67
+ parseAnySource: format.parseAnySource,
68
+ v3CostOf: model.v3CostOf,
69
+ v3ScoreOf: model.v3ScoreOf,
67
70
  proposeFigure: propose.proposeFigure,
68
71
  compile: model.compile,
69
72
  costOf: model.costOf,
@@ -145,6 +148,20 @@ async function resolveFigureTarget(figureId, domainId, host) {
145
148
  * 찾은 뒤에만 보며, Figure의 불변 type과 형식 검증을 다시 확인한다. 검증되지 않은 큰 객체를 LLM
146
149
  * 프롬프트로 옮기면 비용만 태우고, DB에 있던 Figure와 다른 것을 고친다고 잘못 말할 수 있다.
147
150
  */
151
+ /** A V3 asset names its format in `version`; a V2 FigureSource carries a number there. */
152
+ const isV3 = (source) => source?.version === 'figure-v3-asset-1';
153
+ /**
154
+ * The V2 proposer writes V2 figures; a V3 figure open in the modeller is not something it can revise or add to. Said as
155
+ * what is true, with what the assistant can do instead -- not a V2 parser's complaint about a V3 asset (user,
156
+ * 2026-09-24: the dock answered 「type 이 없다 · base 가 없다」 for a V3 draft).
157
+ */
158
+ function v3ProposalUnavailable() {
159
+ return {
160
+ error: 'V3_PROPOSAL_UNAVAILABLE',
161
+ message: '이 도형은 V3 형식입니다. AI 후보 제안은 아직 V2 도형만 만들 수 있어 이 도형을 고치는 후보를 낼 수 없습니다.',
162
+ nextStep: 'Tell the person plainly that candidate proposals do not support V3 figures yet. You can still read the figure (getFigureSource) and explain whether it may be released and why (inspectFigure). Do not produce a V2 candidate for a V3 figure.'
163
+ };
164
+ }
148
165
  function sourceForTool(row, host) {
149
166
  const isMatch = host?.figureId === row.id || (!row.id && host?.editorMode === 'new') || (!row.id && !host?.figureId);
150
167
  const draft = isMatch ? host?.draftSource : undefined;
@@ -158,6 +175,17 @@ function sourceForTool(row, host) {
158
175
  if (serialized.length > 100_000) {
159
176
  throw new Error('the unsaved draft is too large to safely review');
160
177
  }
178
+ /* A V3 draft is checked as V3 -- the same door a save goes through. It names no type; the row does. */
179
+ if (isV3(draft)) {
180
+ try {
181
+ getDeps().parseAnySource(serialized);
182
+ }
183
+ catch (e) {
184
+ const findings = Array.isArray(e?.findings) ? e.findings.map((one) => one.message).join(' · ') : String(e?.message ?? e);
185
+ throw new Error(`the unsaved V3 draft is invalid: ${findings}`);
186
+ }
187
+ return { source: draft, draft: true };
188
+ }
161
189
  const { validate, isPlaceholderType } = getDeps();
162
190
  const checked = validate(draft);
163
191
  if (checked.errors.length > 0) {
@@ -287,6 +315,21 @@ function figureToolSpecs() {
287
315
  // 저장된 정본이 없는 상태가 실제로 있다(새로 만들고 저장 안 한 것). 지어내지 않는다.
288
316
  return { id: row.id, type: row.type, name: row.name, source: null, cost: null };
289
317
  }
318
+ if (isV3(current.source)) {
319
+ /* A V3 asset is measured as V3 -- triangles, draw groups, and its grade against one screen. */
320
+ const { v3CostOf, v3ScoreOf } = getDeps();
321
+ return {
322
+ id: row.id,
323
+ type: row.type,
324
+ name: row.name,
325
+ state: row.state,
326
+ format: 'v3',
327
+ source: current.source,
328
+ draft: current.draft,
329
+ cost: v3CostOf(current.source),
330
+ score: v3ScoreOf(current.source) ?? null
331
+ };
332
+ }
290
333
  const { compile, costOf, scoreOf } = getDeps();
291
334
  const blueprint = compile(current.source);
292
335
  return {
@@ -318,7 +361,7 @@ function figureToolSpecs() {
318
361
  return missingFigureTarget();
319
362
  const current = sourceForTool(row, ctx?.host);
320
363
  const { inspectSource } = getDeps();
321
- return { ...inspectSource(current.source ? JSON.stringify(current.source) : null), draft: current.draft };
364
+ return { ...inspectSource(current.source ? JSON.stringify(current.source) : null, null, undefined, row.type), draft: current.draft };
322
365
  }
323
366
  },
324
367
  {
@@ -369,6 +412,8 @@ function figureToolSpecs() {
369
412
  base = sourceForTool(row, ctx?.host).source ?? undefined;
370
413
  }
371
414
  }
415
+ if (isV3(base) || (args?.create !== true && isV3(ctx?.host?.draftSource)))
416
+ return v3ProposalUnavailable();
372
417
  const palette = Array.isArray(ctx?.host?.palette) ? ctx.host.palette : [];
373
418
  if (palette.length === 0) {
374
419
  /*
@@ -1 +1 @@
1
- {"version":3,"file":"figure-tools.js","sourceRoot":"","sources":["../../../server/service/figure/figure-tools.ts"],"names":[],"mappings":";;AA4EA,gEAEC;AAqZD,oEAuBC;AA1fD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,mEAAuG;AAEvG;;;EAGE;AACF,IAAI,KAAsB,CAAA;AAE1B,SAAS,OAAO;IACd,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,8DAA8D;QAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAA;QAC9C,8DAA8D;QAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAA;QAC/C,8DAA8D;QAC9D,MAAM,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;QACrC,8DAA8D;QAC9D,MAAM,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAA;QAC5C,8DAA8D;QAC9D,MAAM,OAAO,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAA;QAC9C,KAAK,GAAG;YACN,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB;;;cAGE;YACF,aAAa,EAAE,MAAM,CAAC,gBAAgB;YACtC,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;SAC3C,CAAA;IACH,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,yCAAyC;AACzC,SAAgB,0BAA0B,CAAC,IAAS;IAClD,KAAK,GAAG,IAAI,CAAA;AACd,CAAC;AAED,SAAS,eAAe,CAAC,KAAU;IACjC,MAAM,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,EAAE,CAAA;IAC5B,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,4CAA4C;QAC5C,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAA;IAC7F,CAAC;IACD,OAAO,EAAE,CAAA;AACX,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,cAAc,CAAC,QAAgB,EAAE,QAAgB;IAC9D,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,CAAA;IAC3C,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;QAChD,KAAK,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE;KAClD,CAAC,CAAA;IACF,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,oCAAoC,QAAQ,EAAE,CAAC,CAAA;IACjE,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,mBAAmB,CAAC,QAA4B,EAAE,QAAgB,EAAE,IAAS;IAC1F,IACE,IAAI,EAAE,UAAU,KAAK,KAAK;QAC1B,IAAI,EAAE,WAAW;QACjB,CAAC,CAAC,QAAQ,IAAI,QAAQ,KAAK,IAAI,CAAC,WAAW,CAAC,IAAI,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,EAClF,CAAC;QACD,OAAO,aAAa,CAAC,IAAI,CAAC,CAAA;IAC5B,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,CAAA;QAC3C,IAAI,KAAK,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;YAC9C,KAAK,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE;SAClD,CAAC,CAAA;QACF,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;gBAC1C,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE;aACpD,CAAC,CAAA;QACJ,CAAC;QACD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IACE,IAAI,EAAE,UAAU,KAAK,KAAK;gBAC1B,CAAC,IAAI,EAAE,WAAW;oBAChB,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,QAAQ;wBACjC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC;wBAC7B,CAAC,IAAI,CAAC,QAAQ;wBACd,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,EAChC,CAAC;gBACD,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,CAAA;gBACnC,IAAI,OAAO;oBAAE,OAAO,OAAO,CAAA;YAC7B,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,oCAAoC,QAAQ,EAAE,CAAC,CAAA;QACjE,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,OAAO,aAAa,CAAC,IAAI,CAAC,CAAA;AAC5B,CAAC;AAED;;;;;;GAMG;AACH,SAAS,aAAa,CAAC,GAAQ,EAAE,IAAS;IACxC,MAAM,OAAO,GAAG,IAAI,EAAE,QAAQ,KAAK,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,IAAI,EAAE,UAAU,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IACpH,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,SAAS,CAAA;IACrD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IAC7E,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAA;IACnE,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IACxC,IAAI,UAAU,CAAC,MAAM,GAAG,OAAO,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAA;IACpE,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,iBAAiB,EAAE,GAAG,OAAO,EAAE,CAAA;IACjD,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC/B,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,iCAAiC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC/G,CAAC;IAED,IAAI,eAAe,GAAG,KAAK,CAAA;IAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC;QAC5B,2DAA2D;QAC3D,uCAAuC;QACvC,MAAM,mBAAmB,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACnG,IAAI,mBAAmB,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;YACpC,eAAe,GAAG,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAA;QAChD,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,2BAA2B,KAAK,CAAC,IAAI,4BAA4B,GAAG,CAAC,IAAI,GAAG,CAAC,CAAA;QAC/F,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;AACjD,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CAAC,IAAS,EAAE,QAAiB;IACnD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,gBAAgB,CAAC;QAAE,OAAO,SAAS,CAAA;IAC5D,IAAI,QAAQ,IAAI,IAAI,EAAE,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC5C,OAAO,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IACxC,CAAC;IACD,IAAI,IAAI,EAAE,UAAU,KAAK,KAAK,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC;QAClD,OAAO,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IACxC,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,IAAS,EAAE,IAAS;IACxC,MAAM,IAAI,GAAG,OAAO,IAAI,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IAC3E,IAAI,IAAI;QAAE,OAAO,IAAI,CAAA;IAErB,MAAM,IAAI,GAAG,OAAO,IAAI,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IAC3E,IAAI,IAAI;QAAE,OAAO,IAAI,CAAA;IAErB,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAS,mBAAmB;IAC1B,OAAO;QACL,KAAK,EAAE,wBAAwB;QAC/B,OAAO,EAAE,uBAAuB;QAChC,QAAQ,EAAE,4QAA4Q;KACvR,CAAA;AACH,CAAC;AAED,SAAS,aAAa,CAAC,IAAS;IAC9B,IAAI,IAAI,EAAE,UAAU,KAAK,KAAK,IAAI,CAAC,IAAI,EAAE,WAAW;QAAE,OAAO,SAAS,CAAA;IACtE,MAAM,GAAG,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,IAAI,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAA;IACpG,+EAA+E;IAC/E,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IACxC,OAAO,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAA;AAC3D,CAAC;AAED,MAAM,aAAa,GAAG;IACpB,QAAQ,EAAE;QACR,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,yOAAyO;KACvP;CACO,CAAA;AAEV;;;;;GAKG;AACH,SAAS,eAAe;IACtB,OAAO;QACL;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,aAAa;YACnB,kDAAkD;YAClD,KAAK,EAAE,CAAC,eAAe,CAAC;YACxB,WAAW,EACT,kFAAkF;gBAClF,4FAA4F;YAC9F,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE;YAC1C,OAAO,EAAE,KAAK,EAAE,KAAU,EAAE,GAAQ,EAAE,EAAE;gBACtC,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;gBAC5C,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,CAAA;gBAC3C,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;oBAC5C,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE;oBACnC,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;oBAC5B,IAAI,EAAE,GAAG;iBACV,CAAC,CAAA;gBACF,OAAO;oBACL,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,CAAC;wBAC/B,EAAE,EAAE,GAAG,CAAC,EAAE;wBACV,IAAI,EAAE,GAAG,CAAC,IAAI;wBACd,IAAI,EAAE,GAAG,CAAC,IAAI;wBACd,KAAK,EAAE,GAAG,CAAC,KAAK;wBAChB,KAAK,EAAE,GAAG,CAAC,KAAK;wBAChB,SAAS,EAAE,GAAG,CAAC,SAAS;qBACzB,CAAC,CAAC;iBACJ,CAAA;YACH,CAAC;SACF;QACD;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,iBAAiB;YACvB;yFAC6E;YAC7E,KAAK,EAAE,CAAC,cAAc,CAAC;YACvB,WAAW,EACT,2EAA2E;gBAC3E,+GAA+G;gBAC/G,8DAA8D;YAChE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,GAAG,aAAa,EAAE,EAAE;YAC5D,OAAO,EAAE,KAAK,EAAE,IAAS,EAAE,GAAQ,EAAE,EAAE;gBACrC,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;gBAC5C,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;gBAC9C,MAAM,GAAG,GAAG,MAAM,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;gBACpE,IAAI,CAAC,GAAG;oBAAE,OAAO,mBAAmB,EAAE,CAAA;gBAEtC,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;gBAC7C,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;oBACpB,oDAAoD;oBACpD,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;gBACjF,CAAC;gBAED,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAA;gBAC9C,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;gBAEzC,OAAO;oBACL,EAAE,EAAE,GAAG,CAAC,EAAE;oBACV,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,KAAK,EAAE,GAAG,CAAC,KAAK;oBAChB,MAAM,EAAE,OAAO,CAAC,MAAM;oBACtB,KAAK,EAAE,OAAO,CAAC,KAAK;oBACpB,IAAI,EAAE,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC;oBAC5B,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC;iBAC1B,CAAA;YACH,CAAC;SACF;QACD;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,eAAe;YACrB,yBAAyB;YACzB,KAAK,EAAE,CAAC,qBAAqB,CAAC;YAC9B,WAAW,EACT,2FAA2F;gBAC3F,yFAAyF;gBACzF,mDAAmD;YACrD,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,GAAG,aAAa,EAAE,EAAE;YAC5D,OAAO,EAAE,KAAK,EAAE,IAAS,EAAE,GAAQ,EAAE,EAAE;gBACrC,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;gBAC5C,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;gBAC9C,MAAM,GAAG,GAAG,MAAM,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;gBACpE,IAAI,CAAC,GAAG;oBAAE,OAAO,mBAAmB,EAAE,CAAA;gBAEtC,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;gBAC7C,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAA;gBACnC,OAAO,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAA;YAC3G,CAAC;SACF;QACD;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,wBAAwB;YAC9B,sCAAsC;YACtC,KAAK,EAAE,CAAC,wBAAwB,CAAC;YACjC,WAAW,EACT,uFAAuF;gBACvF,0FAA0F;gBAC1F,6EAA6E;YAC/E,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,MAAM,EAAE;wBACN,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,uDAAuD;qBACrE;oBACD,QAAQ,EAAE;wBACR,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,0FAA0F;qBACxG;oBACD,IAAI,EAAE;wBACJ,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,kFAAkF;qBAChG;oBACD,MAAM,EAAE;wBACN,IAAI,EAAE,SAAS;wBACf,WAAW,EAAE,6DAA6D;qBAC3E;iBACF;gBACD,QAAQ,EAAE,CAAC,QAAQ,CAAC;aACrB;YACD,OAAO,EAAE,KAAK,EAAE,IAAS,EAAE,GAAQ,EAAE,EAAE;gBACrC,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;gBAC5C,MAAM,MAAM,GAAG,OAAO,IAAI,EAAE,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;gBACzE,IAAI,CAAC,MAAM;oBAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAA;gBAE/E;;;;kBAIE;gBACF,IAAI,IAAa,CAAA;gBACjB,IAAI,CAAC,IAAI,EAAE,MAAM,IAAI,IAAI,EAAE,QAAQ,KAAK,IAAI,EAAE,CAAC;oBAC7C,MAAM,IAAI,GAAG,OAAO,IAAI,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAA;oBACrF,MAAM,GAAG,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;oBAC1F,IAAI,GAAG,EAAE,CAAC;wBACR,IAAI,GAAG,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,MAAM,IAAI,SAAS,CAAA;oBAC1D,CAAC;gBACH,CAAC;gBAED,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;gBACzE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACzB;;;sBAGE;oBACF,MAAM,IAAI,KAAK,CAAC,mFAAmF,CAAC,CAAA;gBACtG,CAAC;gBAED,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAA;gBACnC,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;gBAE7F,IAAI,SAAuD,CAAA;gBAC3D,MAAM,QAAQ,GAAG,GAAG,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;gBACpK,IAAI,QAAQ,EAAE,IAAI,EAAE,CAAC;oBACnB,MAAM,OAAO,GAAG,OAAO,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,gCAAgC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAA;oBAC/H,MAAM,GAAG,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;oBAC/F,SAAS,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,IAAI,WAAW,EAAE,CAAA;gBACzE,CAAC;gBAED,IAAI,MAAW,CAAA;gBACf,IAAI,CAAC;oBACH,MAAM,GAAG,MAAM,aAAa,CAAC;wBAC3B,MAAM;wBACN,OAAO;wBACP,IAAI;wBACJ,IAAI,EAAE,OAAO,IAAI,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;wBAC5D,QAAQ;wBACR,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM;wBAC1B,KAAK,EAAE,SAAS;qBACjB,CAAC,CAAA;gBACJ,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf;;;;;uBAKG;oBACH,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAE,KAAa,EAAE,OAAO,CAAC,CAAC,CAAC,CAAE,KAAa,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;oBACpF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;wBAAE,MAAM,IAAI,KAAK,CAAC,CAAE,KAAe,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;oBAC1F,MAAM,KAAK,CAAA;gBACb,CAAC;gBAED;;;;;;;kBAOE;gBACF,OAAO;oBACL,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,MAAM;oBACvE,MAAM,EACJ,MAAM,MAAM,CAAC,KAAK,CAAC,KAAK,UAAU,MAAM,CAAC,IAAI,CAAC,SAAS,KAAK;wBAC5D,QAAQ,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,OAAO,MAAM,CAAC,QAAQ,SAAS;oBACjE,OAAO,EAAE;wBACP,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;wBAC3E,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,iCAAiC,EAAE;wBACtH,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,kCAAkC,EAAE;wBACrH,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,2BAA2B,EAAE;wBAC5G,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,8BAA8B,EAAE;qBAC/G;oBACD,IAAI,EAAE;wBACJ,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;wBACnD,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;wBAClE,SAAS,EAAE,IAAI,IAAI,GAAG,EAAE,IAAI,EAAE,UAAU,KAAK,KAAK,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC;4BAClF,CAAC,CAAE,IAAY,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;qBACnC;oBACD,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,OAAO,EAAE,MAAM,CAAC,OAAO;oBACvB,UAAU,EAAE,MAAM,CAAC,UAAU;oBAC7B,QAAQ,EAAE,MAAM,CAAC,QAAQ;iBAC1B,CAAA;YACH,CAAC;SACF;KACY,CAAA;AACjB,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,4BAA4B;IAC1C,MAAM,SAAS,GAAiB;QAC9B,IAAI,EAAE,kBAAkB;QACxB,WAAW,EAAE,8CAA8C;QAC3D,QAAQ,EAAE;YACR,iEAAiE;YACjE,wBAAwB;YACxB,mGAAmG;YACnG,yEAAyE;YACzE,0EAA0E;YAC1E,oDAAoD;YACpD,qEAAqE;YACrE,yGAAyG;YACzG,8KAA8K;YAC9K,qFAAqF;YACrF,yEAAyE;YACzE,gGAAgG;YAChG,gEAAgE;SACjE,CAAC,IAAI,CAAC,IAAI,CAAC;QACZ,KAAK,EAAE,eAAe,EAAE;KACzB,CAAA;IAED,IAAA,qCAAoB,EAAC,SAAS,CAAC,CAAA;AACjC,CAAC","sourcesContent":["/*\n * figure 저작 도구를 어시스턴트 도구 레지스트리에 등록 — **대화면(도크)에서 저작을 돕기 위함.**\n *\n * 왜 필요한가: `proposeFigure` 는 한 번 시키고 한 번 받는다. 그것으로 되는 일이 있지만, 저작은\n * 대개 「이걸 이렇게 고쳐 봐 → 무게가 얼마나 되나 → 발행은 되나」처럼 여러 차례 오간다. 그 대화를\n * 하려면 어시스턴트가 **지금 무엇이 있는지 읽을 수 있어야** 한다. 읽지 못하면 매번 사람이 정본을\n * 붙여 넣어야 하고, 그러면 대화가 아니다.\n *\n * 능력을 복제하지 않는다. figure-service 가 이미 가진 진입점(`inspectSource` · `proposeFigure` ·\n * 조회)을 레지스트리에 투영한다.\n *\n * ── 전부 조회·제안 전용이다 ─────────────────────────────────────────────────\n * 정본을 고치는 도구를 넣지 않는다. 형식의 설계가 그렇게 정해져 있다 — 어시스턴트는 **후보**를\n * 내고, 받을지 버릴지는 저작자가 정한다(`figure-propose.ts`). 되돌릴 수 없는 조작이 대화면으로\n * 새지 않으므로 전부 `kind: 'read'` 다.\n *\n * 저장은 저작면이 `updateFigure` 로 한다. 그 mutation 은 `figure:mutation` 이 지키고, 대화는\n * `ai-assistant:mutation` 이 지킨다 — 서로 다른 질문이라 서로 다른 권한이 지킨다. figure 가 board\n * 의 권한을 빌리지 않는다.\n *\n * ── No privilege here. The gate reads what the door declares ─────────────────────\n * Until 2026-09-18 each tool carried `privilege: { category: 'figure', ... }`. It matched the door's\n * directive then, and would drift the day the door changed its declaration — and the looser copy wins.\n * Now the gate reads the declaration of each door in `doors` from AUTH's table (ADR-0071 decision 2,\n * addendum). What stays here is **the doors it reaches**.\n *\n * ── 규율을 도구와 함께 보낸다 ───────────────────────────────────────────────\n * 도구 목록만으로는 「언제 반드시 불러야 하는지」를 모른다. twin 이 그 공백에서 사고를 냈다 —\n * 조치 요청에 도구를 부르지 않고 「제안해 두었습니다」라고만 답해, 사용자에게 **없는 버튼을 누르라고\n * 안내했다.** figure 도 똑같은 자리가 있다: 후보 화면(「지금 / 후보」 두 칸)은 **도구 호출 결과에서만**\n * 만들어진다. 문장으로는 아무 일도 일어나지 않는다.\n *\n * ── LLM 이 넘긴 id 를 믿지 않는다 ───────────────────────────────────────────\n * 사람이 목록에서 고르는 길에서는 드러나지 않지만, 모델은 대화 내용에 이끌려 임의의 id 를 넘길 수\n * 있다. 그래서 호출자 도메인 소속을 먼저 확인한다.\n */\nimport { registerToolCategory, type ToolCategory, type ToolSpec } from '@things-factory/ai-client-base'\n\n/*\n 서버 의존성은 lazy require 다. 등록은 부팅 초반에 평가되고 그때 typeorm DataSource 가 준비되지\n 않았을 수 있다(board-import · twin-ai 와 같은 정책).\n*/\nlet _deps: any | undefined\n\nfunction getDeps(): any {\n if (!_deps) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const shell = require('@things-factory/shell')\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const model = require('@hatiolab/figure-model')\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const entity = require('./figure.js')\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const format = require('./figure-format.js')\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const propose = require('./figure-propose.js')\n _deps = {\n getRepository: shell.getRepository,\n Figure: entity.Figure,\n /*\n The format-aware door, so an AI asked about a V3 model gets the V3 judgement rather than a V2 parser's\n complaint. The tool's own contract is unchanged: it reads, it never saves (ADR-0087).\n */\n inspectSource: format.inspectAnySource,\n proposeFigure: propose.proposeFigure,\n compile: model.compile,\n costOf: model.costOf,\n scoreOf: model.scoreOf,\n validate: model.validate,\n isPlaceholderType: model.isPlaceholderType\n }\n }\n return _deps\n}\n\n/** 시험·검증 하네스 전용 주입 시임. 운영 경로에서 호출 금지. */\nexport function __setFigureToolDepsForTest(deps: any): void {\n _deps = deps\n}\n\nfunction requireDomainId(state: any): string {\n const id = state?.domain?.id\n if (!id) {\n // 도메인을 모르면 무엇을 읽어도 남의 것일 수 있다. 조용히 넘기지 않는다.\n throw new Error('domain context is missing — the figure tools cannot resolve tenant scope')\n }\n return id\n}\n\n/**\n * 이 도형이 호출자 도메인 것인가.\n *\n * 모델이 넘긴 id 를 그대로 쓰지 않는다. 없으면 못 찾았다고 말한다 — 「권한이 없다」와 구별해\n * 알려 줄 것이 없다(다른 테넌트에 그 id 가 있다는 사실 자체가 알려 줄 것이 아니다).\n */\nasync function figureInDomain(figureId: string, domainId: string): Promise<any> {\n const { getRepository, Figure } = getDeps()\n const found = await getRepository(Figure).findOne({\n where: { id: figureId, domain: { id: domainId } }\n })\n if (!found) {\n throw new Error(`figure not found in this tenant: ${figureId}`)\n }\n return found\n}\n\n/**\n * figureId 또는 hostContext로부터 대상 Figure row(또는 미저장 초안 가상 row)를 해소한다.\n */\nasync function resolveFigureTarget(figureId: string | undefined, domainId: string, host: any): Promise<any> {\n if (\n host?.editorMode === 'new' &&\n host?.draftSource &&\n (!figureId || figureId === host.draftSource.type || figureId.startsWith('FIGURE'))\n ) {\n return unsavedFigure(host)\n }\n\n if (figureId) {\n const { getRepository, Figure } = getDeps()\n let found = await getRepository(Figure).findOne({\n where: { id: figureId, domain: { id: domainId } }\n })\n if (!found) {\n found = await getRepository(Figure).findOne({\n where: { type: figureId, domain: { id: domainId } }\n })\n }\n if (!found) {\n if (\n host?.editorMode === 'new' ||\n (host?.draftSource &&\n (host.draftSource.type === figureId ||\n figureId.startsWith('FIGURE') ||\n !host.figureId ||\n host.figureId === figureId))\n ) {\n const unsaved = unsavedFigure(host)\n if (unsaved) return unsaved\n }\n throw new Error(`figure not found in this tenant: ${figureId}`)\n }\n return found\n }\n\n return unsavedFigure(host)\n}\n\n/**\n * 모델러가 아직 저장하지 않은 source를 도구가 읽는 유일한 길.\n *\n * host context는 브라우저가 보낸 값이므로 권한 근거가 될 수 없다. 먼저 `row`를 현재 도메인에서\n * 찾은 뒤에만 보며, Figure의 불변 type과 형식 검증을 다시 확인한다. 검증되지 않은 큰 객체를 LLM\n * 프롬프트로 옮기면 비용만 태우고, DB에 있던 Figure와 다른 것을 고친다고 잘못 말할 수 있다.\n */\nfunction sourceForTool(row: any, host: any): { source: any; draft: boolean } {\n const isMatch = host?.figureId === row.id || (!row.id && host?.editorMode === 'new') || (!row.id && !host?.figureId)\n const draft = isMatch ? host?.draftSource : undefined\n if (!draft) {\n return { source: row.source ? JSON.parse(row.source) : null, draft: false }\n }\n\n if (typeof draft !== 'object' || Array.isArray(draft)) {\n throw new Error('the unsaved draft is not a FigureSource object')\n }\n\n const serialized = JSON.stringify(draft)\n if (serialized.length > 100_000) {\n throw new Error('the unsaved draft is too large to safely review')\n }\n\n const { validate, isPlaceholderType } = getDeps()\n const checked = validate(draft)\n if (checked.errors.length > 0) {\n throw new Error(`the unsaved draft is invalid: ${checked.errors.map((one: any) => one.message).join(' · ')}`)\n }\n\n let normalizedDraft = draft\n if (draft.type !== row.type) {\n // 템플릿 기본값 'FIGURE', 임시 draftType('FIGURE_...'), 또는 빈 값인 경우\n // 현재 열려 있는 Figure의 실제 type으로 보정/정규화한다.\n const isGenericOrTempType = !draft.type || draft.type === 'FIGURE' || isPlaceholderType(draft.type)\n if (isGenericOrTempType && row.type) {\n normalizedDraft = { ...draft, type: row.type }\n } else {\n throw new Error(`the unsaved draft type '${draft.type}' does not match Figure '${row.type}'`)\n }\n }\n\n return { source: normalizedDraft, draft: true }\n}\n\n/**\n * 후보를 냈던 바로 그 Figure의 세션 반응만 후보 생성기에 넘긴다.\n *\n * 도크의 host context는 브라우저 값이므로 이 함수는 접근 권한을 판단하지 않는다. 이미 현재\n * 도메인에서 Figure를 찾은 뒤, `proposeFigure`가 다시 길이·형식을 정리할 참고 데이터만 건넨다.\n */\nfunction feedbackForTool(host: any, figureId?: string): unknown[] | undefined {\n if (!Array.isArray(host?.proposalFeedback)) return undefined\n if (figureId && host?.figureId === figureId) {\n return host.proposalFeedback.slice(-5)\n }\n if (host?.editorMode === 'new' || !host?.figureId) {\n return host.proposalFeedback.slice(-5)\n }\n return undefined\n}\n\n/**\n * 무엇을 고치라는 것인지 대화면 문맥에서 해소한다.\n *\n * 모델이 대상을 안 줬으면 **저작면이 지금 열고 있는 도형**이다. 사용자에게 식별자를 되묻지\n * 않는다 — 화면에 그 도형이 떠 있는데 「어느 도형입니까」를 묻는 것은 문맥을 안 읽은 것이다.\n */\nfunction figureIdFrom(args: any, host: any): string | undefined {\n const said = typeof args?.figureId === 'string' ? args.figureId.trim() : ''\n if (said) return said\n\n const open = typeof host?.figureId === 'string' ? host.figureId.trim() : ''\n if (open) return open\n\n return undefined\n}\n\nfunction missingFigureTarget() {\n return {\n error: 'FIGURE_TARGET_REQUIRED',\n message: '현재 화면에는 선택된 도형이 없습니다.',\n nextStep: 'For an existing figure, call listFigures and match the user’s requested name or type; ask which figure only if ambiguous. For a new figure, call proposeFigureCandidate with create: true and the user’s prompt. Do not invent an id or repeat this call without a target.'\n }\n}\n\nfunction unsavedFigure(host: any): any | undefined {\n if (host?.editorMode !== 'new' || !host?.draftSource) return undefined\n const row = { id: host.figureId, type: host.draftSource.type || 'FIGURE', name: '', state: 'draft' }\n // Reuse size, shape and vocabulary validation before trusting browser content.\n const current = sourceForTool(row, host)\n return { ...row, source: JSON.stringify(current.source) }\n}\n\nconst FIGURE_ID_ARG = {\n figureId: {\n type: 'string',\n description: 'The figure to read. Omit to read the one the person has open, saved or not — the server fills it in, and you cannot see which it is. On the catalog nothing is open, so resolve the figure the person asked for with listFigures first.'\n }\n} as const\n\n/**\n * 도구 넷.\n *\n * 목록 · 정본 · 발행 판정 · 후보. 저작 대화에서 실제로 묻는 것이 그 넷이다 — 「무엇이 있나」\n * 「지금 어떻게 생겼나」 「발행이 왜 안 되나」 「이렇게 고쳐 봐」.\n */\nfunction figureToolSpecs(): ToolSpec[] {\n return [\n {\n kind: 'read',\n name: 'listFigures',\n /* 이 도메인의 도형 목록 — 저장소를 직접 읽지만 같은 일을 하는 문은 이것이다. */\n doors: ['Query.figures'],\n description:\n 'Lists the figures in this tenant with their type names, states and part counts. ' +\n 'Call it before naming a type — type names are stored identifiers and must not be invented.',\n schema: { type: 'object', properties: {} },\n builder: async (_args: any, ctx: any) => {\n const domainId = requireDomainId(ctx?.state)\n const { getRepository, Figure } = getDeps()\n const rows = await getRepository(Figure).find({\n where: { domain: { id: domainId } },\n order: { updatedAt: 'DESC' },\n take: 200\n })\n return {\n figures: rows.map((row: any) => ({\n id: row.id,\n type: row.type,\n name: row.name,\n state: row.state,\n score: row.score,\n triangles: row.triangles\n }))\n }\n }\n },\n {\n kind: 'read',\n name: 'getFigureSource',\n /* 저장된 도형의 원본. **저장 전 초안**을 물으면 저장소에 갈 것이 없어 화면이 보낸 것을 그대로 돌려준다 —\n * 그 갈래에는 닿는 문이 없지만 `doors: []` 로 적지 않는다. 그러면 저장된 도형을 읽는 권한 검사가 선언에서 사라진다. */\n doors: ['Query.figure'],\n description:\n 'Returns the canonical FigureSource of one figure, with its measured cost ' +\n '(triangles, part groups, and how many of it hold 60fps on one screen). Call it before proposing a revision — ' +\n 'revising without reading it means proposing against a guess.',\n schema: { type: 'object', properties: { ...FIGURE_ID_ARG } },\n builder: async (args: any, ctx: any) => {\n const domainId = requireDomainId(ctx?.state)\n const figureId = figureIdFrom(args, ctx?.host)\n const row = await resolveFigureTarget(figureId, domainId, ctx?.host)\n if (!row) return missingFigureTarget()\n\n const current = sourceForTool(row, ctx?.host)\n if (!current.source) {\n // 저장된 정본이 없는 상태가 실제로 있다(새로 만들고 저장 안 한 것). 지어내지 않는다.\n return { id: row.id, type: row.type, name: row.name, source: null, cost: null }\n }\n\n const { compile, costOf, scoreOf } = getDeps()\n const blueprint = compile(current.source)\n\n return {\n id: row.id,\n type: row.type,\n name: row.name,\n state: row.state,\n source: current.source,\n draft: current.draft,\n cost: costOf(blueprint, 100),\n score: scoreOf(blueprint)\n }\n }\n },\n {\n kind: 'read',\n name: 'inspectFigure',\n /* 검사 결과 — 같은 일을 하는 문. */\n doors: ['Query.inspectFigure'],\n description:\n 'Says whether a figure may be released and, when it may not, what is wrong — each finding ' +\n 'carries why it is a problem and what to change. Call it when asked about releasing, or ' +\n 'when asked to fix a figure that will not release.',\n schema: { type: 'object', properties: { ...FIGURE_ID_ARG } },\n builder: async (args: any, ctx: any) => {\n const domainId = requireDomainId(ctx?.state)\n const figureId = figureIdFrom(args, ctx?.host)\n const row = await resolveFigureTarget(figureId, domainId, ctx?.host)\n if (!row) return missingFigureTarget()\n\n const current = sourceForTool(row, ctx?.host)\n const { inspectSource } = getDeps()\n return { ...inspectSource(current.source ? JSON.stringify(current.source) : null), draft: current.draft }\n }\n },\n {\n kind: 'read',\n name: 'proposeFigureCandidate',\n /* 후보 생성 — 저장은 하지 않지만 같은 문의 권한을 쓴다. */\n doors: ['Mutation.proposeFigure'],\n description:\n 'Asks for a figure candidate from a description, optionally revising an existing one. ' +\n 'Returns the candidate with its score and cost. NOTHING IS SAVED — the author accepts or ' +\n 'discards it on screen. This is the only way a candidate reaches the screen.',\n schema: {\n type: 'object',\n properties: {\n prompt: {\n type: 'string',\n description: 'What to make or change, in the words the author used.'\n },\n figureId: {\n type: 'string',\n description: 'Revise this figure. Omit to revise the one open in the editor; pass null to create anew.'\n },\n type: {\n type: 'string',\n description: 'Type name for a new figure. Check listFigures first — it is a stored identifier.'\n },\n create: {\n type: 'boolean',\n description: 'True to create rather than revise, even with a figure open.'\n }\n },\n required: ['prompt']\n },\n builder: async (args: any, ctx: any) => {\n const domainId = requireDomainId(ctx?.state)\n const prompt = typeof args?.prompt === 'string' ? args.prompt.trim() : ''\n if (!prompt) throw new Error('prompt is required: there is nothing to ask for')\n\n /*\n 고치는 것인지 만드는 것인지를 **문맥에서 정한다.** 열린 도형이 있으면 고치는 것이 기본이고,\n 모델이 `create` 를 켜면 만드는 것이다. 이것을 모델의 짐작에 맡기면 열려 있는 도형을 덮어쓰는\n 후보가 온다.\n */\n let base: unknown\n if (!args?.create && args?.figureId !== null) {\n const open = typeof args?.figureId === 'string' ? args.figureId : ctx?.host?.figureId\n const row = await resolveFigureTarget(open ? open.trim() : undefined, domainId, ctx?.host)\n if (row) {\n base = sourceForTool(row, ctx?.host).source ?? undefined\n }\n }\n\n const palette = Array.isArray(ctx?.host?.palette) ? ctx.host.palette : []\n if (palette.length === 0) {\n /*\n 팔레트를 모르면 모델이 색을 지어내고, 그 후보는 그리는 시점에 `resolveToken` 이 던진다 —\n 저장까지 되고 나서 화면에서 죽는다. 빈 목록으로 진행하지 않는다.\n */\n throw new Error('the palette is missing from the host context — the candidate would invent colours')\n }\n\n const { proposeFigure } = getDeps()\n const feedback = feedbackForTool(ctx?.host, base ? figureIdFrom(args, ctx?.host) : undefined)\n\n let hostImage: { data: Buffer; mediaType: any } | undefined\n const rawImage = ctx?.host?.image || (Array.isArray(ctx?.host?.attachments) ? ctx.host.attachments.find((a: any) => a?.mediaType?.startsWith('image/')) : undefined)\n if (rawImage?.data) {\n const rawData = typeof rawImage.data === 'string' ? rawImage.data.replace(/^data:image\\/[a-zA-Z]+;base64,/, '') : rawImage.data\n const buf = typeof rawData === 'string' ? Buffer.from(rawData, 'base64') : Buffer.from(rawData)\n hostImage = { data: buf, mediaType: rawImage.mediaType || 'image/png' }\n }\n\n let result: any\n try {\n result = await proposeFigure({\n prompt,\n palette,\n base,\n type: typeof args?.type === 'string' ? args.type : undefined,\n feedback,\n refine: !!feedback?.length,\n image: hostImage\n })\n } catch (error) {\n /*\n * 후보 생성은 세 번의 JSON·형식 검사 끝에 실패할 수 있다. 그 마지막 검사 사유를\n * ProposeFailure.reasons 에만 두면 agentic-loop은 \"만들지 못했습니다\"만 받아\n * 사용자 화면에는 \"응답이 비어있습니다\"가 된다. 재시도할 수 있는 말로 경계를\n * 넘긴다. 다른 예외(권한·통신)는 원문을 보존한다.\n */\n const reasons = Array.isArray((error as any)?.reasons) ? (error as any).reasons : []\n if (reasons.length > 0) throw new Error([(error as Error).message, ...reasons].join('\\n'))\n throw error\n }\n\n /*\n **`proposed: true` 가 카드를 만든다.** 이 표시가 없으면 후보는 도구 추적 안에 묻히고,\n 화면에는 아무것도 생기지 않는다 — 그러면 모델이 「후보를 만들었습니다」라고 쓴 것이\n 거짓말이 된다(사용자가 없는 단추를 찾는다).\n\n `label`·`reason` 이 카드에 그려지는 두 줄이고, `args` 는 같은 제안을 두 번 세지 않기\n 위한 열쇠의 일부다 — 같은 요청을 두 번 내면 카드가 하나로 접힌다.\n */\n return {\n proposed: true,\n label: base ? `${result.source.type} 고치기` : `${result.source.type} 만들기`,\n reason:\n `등급 ${result.score.grade} · 삼각형 ${result.cost.triangles} · ` +\n `한 도면 ${result.cost.at.drawCalls}회 · ${result.attempts}번에 만들었다`,\n choices: [\n { id: 'save', action: 'save', label: '초안 저장', icon: 'save', primary: true },\n { id: 'repropose', action: 'prompt', label: '다른 콘셉트 재제안', icon: 'refresh', prompt: '현재 제안과 완전히 다른 스타일과 콘셉트로 새로 제안해줘' },\n { id: 'slim', action: 'prompt', label: '비율 슬림 조정', icon: 'aspect_ratio', prompt: '전체적인 가로세로 비율을 좀 더 슬림하고 날렵하게 조정해줘' },\n { id: 'palette', action: 'prompt', label: '색상 테마 변경', icon: 'palette', prompt: '다른 팔레트 색상 테마로 부품 색상을 변경해줘' },\n { id: 'detail', action: 'prompt', label: '디테일 보강', icon: 'extension', prompt: '주요 디테일과 센서 부품을 좀 더 풍부하게 보강해줘' }\n ],\n args: {\n baseSource: base ? JSON.stringify(base) : undefined,\n prompt, figureId: base ? figureIdFrom(args, ctx?.host) : undefined,\n draftType: base && ctx?.host?.editorMode === 'new' && !figureIdFrom(args, ctx?.host)\n ? (base as any).type : undefined\n },\n source: result.source,\n score: result.score,\n cost: result.cost,\n quality: result.quality,\n violations: result.violations,\n attempts: result.attempts\n }\n }\n }\n ] as ToolSpec[]\n}\n\n/**\n * 등록 — 부팅 시 1회.\n *\n * 범주가 하나다. twin 은 관찰과 생성을 둘로 갈랐는데(운영 협의에 생성 도구가 들려 있으면 대화의\n * 성격이 흐려진다), figure 의 대화면은 저작면 하나뿐이고 그 자리에서 읽기와 후보 요청이 함께 필요하다.\n * 대화면이 늘면 그때 가른다.\n */\nexport function registerFigureToolCategories(): void {\n const authoring: ToolCategory = {\n name: 'figure-authoring',\n description: '저작 중인 Figure 의 정본·비용·발행 판정 조회와 후보 제안(저장 아님).',\n guidance: [\n '- **도형을 만들거나 고쳐 달라는 요청에는 반드시 `proposeFigureCandidate` 를 호출한다.**',\n ' 후보는 도구 호출 결과에서만 생긴다.',\n ' 호출 없이 \"후보를 만들었습니다\" · \"적용 버튼을 누르세요\" 라고 쓰는 것은 **거짓이다** — 화면에 후보 칸이 생기지 않으므로 사용자는 존재하지 않는 버튼을 찾는다.',\n ' 도크에서는 후보 카드의 버튼을 눌러야 모델러 비교 화면이 열린다. 아직 열지 않은 비교 화면이 이미 보인다고 말하지 않는다.',\n ' 요청한 부품만 바뀌었다고 추측하지 않는다. 후보와 원본을 비교한 근거가 없으면 변경 범위는 비교 화면에서 확인하도록 안내한다.',\n '- 정본을 직접 저장하지 않는다. 후보만 내고, 받을지 버릴지는 저작자가 화면에서 정한다.',\n '- 좌표와 크기는 X/Y/Z 축 이름으로 정확하게 표시한다. 확인되지 않은 가로·세로·높이 의미를 임의로 붙이지 않는다.',\n '- **고치라는 요청이면 먼저 `getFigureSource` 를 부른다.** 읽지 않고 고치면 짐작을 상대로 후보를 내게 되고, 부품 이름이 갈려 이미 배치된 보드의 바인딩이 끊긴다.',\n '- 목록 화면에는 열린 도형이 없다. 기존 도형을 조회·수정할 때는 `listFigures`에서 사용자가 말한 이름·타입의 id를 찾는다. 대상이 모호할 때만 어느 도형인지 묻는다. 새 제작 요청에는 기존 정본을 조회하지 않고 `proposeFigureCandidate`를 create: true로 호출한다.',\n '- 타입 이름을 지어내지 않는다 — `listFigures` 로 확인한다. **타입 이름은 저장되는 식별자**이고 보드가 그 이름으로 도형을 찾는다.',\n '- 발행이 안 되는 이유를 물으면 `inspectFigure` 를 부른다. 판정 없이 \"발행할 수 있습니다\"라고 말하지 않는다.',\n ' 판정의 각 항목은 **왜 문제인지와 무엇을 바꾸면 되는지**를 함께 담고 있다. 그대로 전한다 — 요약하면 저작자가 어느 부품의 어느 축을 고칠지 다시 찾아야 한다.',\n '- 무게·삼각형 수를 물으면 `getFigureSource` 의 `cost` 를 쓴다. 눈대중으로 세지 않는다.'\n ].join('\\n'),\n specs: figureToolSpecs()\n }\n\n registerToolCategory(authoring)\n}\n"]}
1
+ {"version":3,"file":"figure-tools.js","sourceRoot":"","sources":["../../../server/service/figure/figure-tools.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * figure 저작 도구를 어시스턴트 도구 레지스트리에 등록 — **대화면(도크)에서 저작을 돕기 위함.**\n *\n * 왜 필요한가: `proposeFigure` 는 한 번 시키고 한 번 받는다. 그것으로 되는 일이 있지만, 저작은\n * 대개 「이걸 이렇게 고쳐 봐 → 무게가 얼마나 되나 → 발행은 되나」처럼 여러 차례 오간다. 그 대화를\n * 하려면 어시스턴트가 **지금 무엇이 있는지 읽을 수 있어야** 한다. 읽지 못하면 매번 사람이 정본을\n * 붙여 넣어야 하고, 그러면 대화가 아니다.\n *\n * 능력을 복제하지 않는다. figure-service 가 이미 가진 진입점(`inspectSource` · `proposeFigure` ·\n * 조회)을 레지스트리에 투영한다.\n *\n * ── 전부 조회·제안 전용이다 ─────────────────────────────────────────────────\n * 정본을 고치는 도구를 넣지 않는다. 형식의 설계가 그렇게 정해져 있다 — 어시스턴트는 **후보**를\n * 내고, 받을지 버릴지는 저작자가 정한다(`figure-propose.ts`). 되돌릴 수 없는 조작이 대화면으로\n * 새지 않으므로 전부 `kind: 'read'` 다.\n *\n * 저장은 저작면이 `updateFigure` 로 한다. 그 mutation 은 `figure:mutation` 이 지키고, 대화는\n * `ai-assistant:mutation` 이 지킨다 — 서로 다른 질문이라 서로 다른 권한이 지킨다. figure 가 board\n * 의 권한을 빌리지 않는다.\n *\n * ── No privilege here. The gate reads what the door declares ─────────────────────\n * Until 2026-09-18 each tool carried `privilege: { category: 'figure', ... }`. It matched the door's\n * directive then, and would drift the day the door changed its declaration — and the looser copy wins.\n * Now the gate reads the declaration of each door in `doors` from AUTH's table (ADR-0071 decision 2,\n * addendum). What stays here is **the doors it reaches**.\n *\n * ── 규율을 도구와 함께 보낸다 ───────────────────────────────────────────────\n * 도구 목록만으로는 「언제 반드시 불러야 하는지」를 모른다. twin 이 그 공백에서 사고를 냈다 —\n * 조치 요청에 도구를 부르지 않고 「제안해 두었습니다」라고만 답해, 사용자에게 **없는 버튼을 누르라고\n * 안내했다.** figure 도 똑같은 자리가 있다: 후보 화면(「지금 / 후보」 두 칸)은 **도구 호출 결과에서만**\n * 만들어진다. 문장으로는 아무 일도 일어나지 않는다.\n *\n * ── LLM 이 넘긴 id 를 믿지 않는다 ───────────────────────────────────────────\n * 사람이 목록에서 고르는 길에서는 드러나지 않지만, 모델은 대화 내용에 이끌려 임의의 id 를 넘길 수\n * 있다. 그래서 호출자 도메인 소속을 먼저 확인한다.\n */\nimport { registerToolCategory, type ToolCategory, type ToolSpec } from '@things-factory/ai-client-base'\n\n/*\n 서버 의존성은 lazy require 다. 등록은 부팅 초반에 평가되고 그때 typeorm DataSource 가 준비되지\n 않았을 수 있다(board-import · twin-ai 와 같은 정책).\n*/\nlet _deps: any | undefined\n\nfunction getDeps(): any {\n if (!_deps) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const shell = require('@things-factory/shell')\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const model = require('@hatiolab/figure-model')\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const entity = require('./figure.js')\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const format = require('./figure-format.js')\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const propose = require('./figure-propose.js')\n _deps = {\n getRepository: shell.getRepository,\n Figure: entity.Figure,\n /*\n The format-aware door, so an AI asked about a V3 model gets the V3 judgement rather than a V2 parser's\n complaint. The tool's own contract is unchanged: it reads, it never saves (ADR-0087).\n */\n inspectSource: format.inspectAnySource,\n parseAnySource: format.parseAnySource,\n v3CostOf: model.v3CostOf,\n v3ScoreOf: model.v3ScoreOf,\n proposeFigure: propose.proposeFigure,\n compile: model.compile,\n costOf: model.costOf,\n scoreOf: model.scoreOf,\n validate: model.validate,\n isPlaceholderType: model.isPlaceholderType\n }\n }\n return _deps\n}\n\n/** 시험·검증 하네스 전용 주입 시임. 운영 경로에서 호출 금지. */\nexport function __setFigureToolDepsForTest(deps: any): void {\n _deps = deps\n}\n\nfunction requireDomainId(state: any): string {\n const id = state?.domain?.id\n if (!id) {\n // 도메인을 모르면 무엇을 읽어도 남의 것일 수 있다. 조용히 넘기지 않는다.\n throw new Error('domain context is missing — the figure tools cannot resolve tenant scope')\n }\n return id\n}\n\n/**\n * 이 도형이 호출자 도메인 것인가.\n *\n * 모델이 넘긴 id 를 그대로 쓰지 않는다. 없으면 못 찾았다고 말한다 — 「권한이 없다」와 구별해\n * 알려 줄 것이 없다(다른 테넌트에 그 id 가 있다는 사실 자체가 알려 줄 것이 아니다).\n */\nasync function figureInDomain(figureId: string, domainId: string): Promise<any> {\n const { getRepository, Figure } = getDeps()\n const found = await getRepository(Figure).findOne({\n where: { id: figureId, domain: { id: domainId } }\n })\n if (!found) {\n throw new Error(`figure not found in this tenant: ${figureId}`)\n }\n return found\n}\n\n/**\n * figureId 또는 hostContext로부터 대상 Figure row(또는 미저장 초안 가상 row)를 해소한다.\n */\nasync function resolveFigureTarget(figureId: string | undefined, domainId: string, host: any): Promise<any> {\n if (\n host?.editorMode === 'new' &&\n host?.draftSource &&\n (!figureId || figureId === host.draftSource.type || figureId.startsWith('FIGURE'))\n ) {\n return unsavedFigure(host)\n }\n\n if (figureId) {\n const { getRepository, Figure } = getDeps()\n let found = await getRepository(Figure).findOne({\n where: { id: figureId, domain: { id: domainId } }\n })\n if (!found) {\n found = await getRepository(Figure).findOne({\n where: { type: figureId, domain: { id: domainId } }\n })\n }\n if (!found) {\n if (\n host?.editorMode === 'new' ||\n (host?.draftSource &&\n (host.draftSource.type === figureId ||\n figureId.startsWith('FIGURE') ||\n !host.figureId ||\n host.figureId === figureId))\n ) {\n const unsaved = unsavedFigure(host)\n if (unsaved) return unsaved\n }\n throw new Error(`figure not found in this tenant: ${figureId}`)\n }\n return found\n }\n\n return unsavedFigure(host)\n}\n\n/**\n * 모델러가 아직 저장하지 않은 source를 도구가 읽는 유일한 길.\n *\n * host context는 브라우저가 보낸 값이므로 권한 근거가 될 수 없다. 먼저 `row`를 현재 도메인에서\n * 찾은 뒤에만 보며, Figure의 불변 type과 형식 검증을 다시 확인한다. 검증되지 않은 큰 객체를 LLM\n * 프롬프트로 옮기면 비용만 태우고, DB에 있던 Figure와 다른 것을 고친다고 잘못 말할 수 있다.\n */\n/** A V3 asset names its format in `version`; a V2 FigureSource carries a number there. */\nconst isV3 = (source: any) => source?.version === 'figure-v3-asset-1'\n\n/**\n * The V2 proposer writes V2 figures; a V3 figure open in the modeller is not something it can revise or add to. Said as\n * what is true, with what the assistant can do instead -- not a V2 parser's complaint about a V3 asset (user,\n * 2026-09-24: the dock answered 「type 이 없다 · base 가 없다」 for a V3 draft).\n */\nfunction v3ProposalUnavailable() {\n return {\n error: 'V3_PROPOSAL_UNAVAILABLE',\n message: '이 도형은 V3 형식입니다. AI 후보 제안은 아직 V2 도형만 만들 수 있어 이 도형을 고치는 후보를 낼 수 없습니다.',\n nextStep:\n 'Tell the person plainly that candidate proposals do not support V3 figures yet. You can still read the figure (getFigureSource) and explain whether it may be released and why (inspectFigure). Do not produce a V2 candidate for a V3 figure.'\n }\n}\n\nfunction sourceForTool(row: any, host: any): { source: any; draft: boolean } {\n const isMatch = host?.figureId === row.id || (!row.id && host?.editorMode === 'new') || (!row.id && !host?.figureId)\n const draft = isMatch ? host?.draftSource : undefined\n if (!draft) {\n return { source: row.source ? JSON.parse(row.source) : null, draft: false }\n }\n\n if (typeof draft !== 'object' || Array.isArray(draft)) {\n throw new Error('the unsaved draft is not a FigureSource object')\n }\n\n const serialized = JSON.stringify(draft)\n if (serialized.length > 100_000) {\n throw new Error('the unsaved draft is too large to safely review')\n }\n\n /* A V3 draft is checked as V3 -- the same door a save goes through. It names no type; the row does. */\n if (isV3(draft)) {\n try {\n getDeps().parseAnySource(serialized)\n } catch (e: any) {\n const findings = Array.isArray(e?.findings) ? e.findings.map((one: any) => one.message).join(' · ') : String(e?.message ?? e)\n throw new Error(`the unsaved V3 draft is invalid: ${findings}`)\n }\n return { source: draft, draft: true }\n }\n\n const { validate, isPlaceholderType } = getDeps()\n const checked = validate(draft)\n if (checked.errors.length > 0) {\n throw new Error(`the unsaved draft is invalid: ${checked.errors.map((one: any) => one.message).join(' · ')}`)\n }\n\n let normalizedDraft = draft\n if (draft.type !== row.type) {\n // 템플릿 기본값 'FIGURE', 임시 draftType('FIGURE_...'), 또는 빈 값인 경우\n // 현재 열려 있는 Figure의 실제 type으로 보정/정규화한다.\n const isGenericOrTempType = !draft.type || draft.type === 'FIGURE' || isPlaceholderType(draft.type)\n if (isGenericOrTempType && row.type) {\n normalizedDraft = { ...draft, type: row.type }\n } else {\n throw new Error(`the unsaved draft type '${draft.type}' does not match Figure '${row.type}'`)\n }\n }\n\n return { source: normalizedDraft, draft: true }\n}\n\n/**\n * 후보를 냈던 바로 그 Figure의 세션 반응만 후보 생성기에 넘긴다.\n *\n * 도크의 host context는 브라우저 값이므로 이 함수는 접근 권한을 판단하지 않는다. 이미 현재\n * 도메인에서 Figure를 찾은 뒤, `proposeFigure`가 다시 길이·형식을 정리할 참고 데이터만 건넨다.\n */\nfunction feedbackForTool(host: any, figureId?: string): unknown[] | undefined {\n if (!Array.isArray(host?.proposalFeedback)) return undefined\n if (figureId && host?.figureId === figureId) {\n return host.proposalFeedback.slice(-5)\n }\n if (host?.editorMode === 'new' || !host?.figureId) {\n return host.proposalFeedback.slice(-5)\n }\n return undefined\n}\n\n/**\n * 무엇을 고치라는 것인지 대화면 문맥에서 해소한다.\n *\n * 모델이 대상을 안 줬으면 **저작면이 지금 열고 있는 도형**이다. 사용자에게 식별자를 되묻지\n * 않는다 — 화면에 그 도형이 떠 있는데 「어느 도형입니까」를 묻는 것은 문맥을 안 읽은 것이다.\n */\nfunction figureIdFrom(args: any, host: any): string | undefined {\n const said = typeof args?.figureId === 'string' ? args.figureId.trim() : ''\n if (said) return said\n\n const open = typeof host?.figureId === 'string' ? host.figureId.trim() : ''\n if (open) return open\n\n return undefined\n}\n\nfunction missingFigureTarget() {\n return {\n error: 'FIGURE_TARGET_REQUIRED',\n message: '현재 화면에는 선택된 도형이 없습니다.',\n nextStep: 'For an existing figure, call listFigures and match the user’s requested name or type; ask which figure only if ambiguous. For a new figure, call proposeFigureCandidate with create: true and the user’s prompt. Do not invent an id or repeat this call without a target.'\n }\n}\n\nfunction unsavedFigure(host: any): any | undefined {\n if (host?.editorMode !== 'new' || !host?.draftSource) return undefined\n const row = { id: host.figureId, type: host.draftSource.type || 'FIGURE', name: '', state: 'draft' }\n // Reuse size, shape and vocabulary validation before trusting browser content.\n const current = sourceForTool(row, host)\n return { ...row, source: JSON.stringify(current.source) }\n}\n\nconst FIGURE_ID_ARG = {\n figureId: {\n type: 'string',\n description: 'The figure to read. Omit to read the one the person has open, saved or not — the server fills it in, and you cannot see which it is. On the catalog nothing is open, so resolve the figure the person asked for with listFigures first.'\n }\n} as const\n\n/**\n * 도구 넷.\n *\n * 목록 · 정본 · 발행 판정 · 후보. 저작 대화에서 실제로 묻는 것이 그 넷이다 — 「무엇이 있나」\n * 「지금 어떻게 생겼나」 「발행이 왜 안 되나」 「이렇게 고쳐 봐」.\n */\nfunction figureToolSpecs(): ToolSpec[] {\n return [\n {\n kind: 'read',\n name: 'listFigures',\n /* 이 도메인의 도형 목록 — 저장소를 직접 읽지만 같은 일을 하는 문은 이것이다. */\n doors: ['Query.figures'],\n description:\n 'Lists the figures in this tenant with their type names, states and part counts. ' +\n 'Call it before naming a type — type names are stored identifiers and must not be invented.',\n schema: { type: 'object', properties: {} },\n builder: async (_args: any, ctx: any) => {\n const domainId = requireDomainId(ctx?.state)\n const { getRepository, Figure } = getDeps()\n const rows = await getRepository(Figure).find({\n where: { domain: { id: domainId } },\n order: { updatedAt: 'DESC' },\n take: 200\n })\n return {\n figures: rows.map((row: any) => ({\n id: row.id,\n type: row.type,\n name: row.name,\n state: row.state,\n score: row.score,\n triangles: row.triangles\n }))\n }\n }\n },\n {\n kind: 'read',\n name: 'getFigureSource',\n /* 저장된 도형의 원본. **저장 전 초안**을 물으면 저장소에 갈 것이 없어 화면이 보낸 것을 그대로 돌려준다 —\n * 그 갈래에는 닿는 문이 없지만 `doors: []` 로 적지 않는다. 그러면 저장된 도형을 읽는 권한 검사가 선언에서 사라진다. */\n doors: ['Query.figure'],\n description:\n 'Returns the canonical FigureSource of one figure, with its measured cost ' +\n '(triangles, part groups, and how many of it hold 60fps on one screen). Call it before proposing a revision — ' +\n 'revising without reading it means proposing against a guess.',\n schema: { type: 'object', properties: { ...FIGURE_ID_ARG } },\n builder: async (args: any, ctx: any) => {\n const domainId = requireDomainId(ctx?.state)\n const figureId = figureIdFrom(args, ctx?.host)\n const row = await resolveFigureTarget(figureId, domainId, ctx?.host)\n if (!row) return missingFigureTarget()\n\n const current = sourceForTool(row, ctx?.host)\n if (!current.source) {\n // 저장된 정본이 없는 상태가 실제로 있다(새로 만들고 저장 안 한 것). 지어내지 않는다.\n return { id: row.id, type: row.type, name: row.name, source: null, cost: null }\n }\n\n if (isV3(current.source)) {\n /* A V3 asset is measured as V3 -- triangles, draw groups, and its grade against one screen. */\n const { v3CostOf, v3ScoreOf } = getDeps()\n return {\n id: row.id,\n type: row.type,\n name: row.name,\n state: row.state,\n format: 'v3',\n source: current.source,\n draft: current.draft,\n cost: v3CostOf(current.source),\n score: v3ScoreOf(current.source) ?? null\n }\n }\n\n const { compile, costOf, scoreOf } = getDeps()\n const blueprint = compile(current.source)\n\n return {\n id: row.id,\n type: row.type,\n name: row.name,\n state: row.state,\n source: current.source,\n draft: current.draft,\n cost: costOf(blueprint, 100),\n score: scoreOf(blueprint)\n }\n }\n },\n {\n kind: 'read',\n name: 'inspectFigure',\n /* 검사 결과 — 같은 일을 하는 문. */\n doors: ['Query.inspectFigure'],\n description:\n 'Says whether a figure may be released and, when it may not, what is wrong — each finding ' +\n 'carries why it is a problem and what to change. Call it when asked about releasing, or ' +\n 'when asked to fix a figure that will not release.',\n schema: { type: 'object', properties: { ...FIGURE_ID_ARG } },\n builder: async (args: any, ctx: any) => {\n const domainId = requireDomainId(ctx?.state)\n const figureId = figureIdFrom(args, ctx?.host)\n const row = await resolveFigureTarget(figureId, domainId, ctx?.host)\n if (!row) return missingFigureTarget()\n\n const current = sourceForTool(row, ctx?.host)\n const { inspectSource } = getDeps()\n return { ...inspectSource(current.source ? JSON.stringify(current.source) : null, null, undefined, row.type), draft: current.draft }\n }\n },\n {\n kind: 'read',\n name: 'proposeFigureCandidate',\n /* 후보 생성 — 저장은 하지 않지만 같은 문의 권한을 쓴다. */\n doors: ['Mutation.proposeFigure'],\n description:\n 'Asks for a figure candidate from a description, optionally revising an existing one. ' +\n 'Returns the candidate with its score and cost. NOTHING IS SAVED — the author accepts or ' +\n 'discards it on screen. This is the only way a candidate reaches the screen.',\n schema: {\n type: 'object',\n properties: {\n prompt: {\n type: 'string',\n description: 'What to make or change, in the words the author used.'\n },\n figureId: {\n type: 'string',\n description: 'Revise this figure. Omit to revise the one open in the editor; pass null to create anew.'\n },\n type: {\n type: 'string',\n description: 'Type name for a new figure. Check listFigures first — it is a stored identifier.'\n },\n create: {\n type: 'boolean',\n description: 'True to create rather than revise, even with a figure open.'\n }\n },\n required: ['prompt']\n },\n builder: async (args: any, ctx: any) => {\n const domainId = requireDomainId(ctx?.state)\n const prompt = typeof args?.prompt === 'string' ? args.prompt.trim() : ''\n if (!prompt) throw new Error('prompt is required: there is nothing to ask for')\n\n /*\n 고치는 것인지 만드는 것인지를 **문맥에서 정한다.** 열린 도형이 있으면 고치는 것이 기본이고,\n 모델이 `create` 를 켜면 만드는 것이다. 이것을 모델의 짐작에 맡기면 열려 있는 도형을 덮어쓰는\n 후보가 온다.\n */\n let base: unknown\n if (!args?.create && args?.figureId !== null) {\n const open = typeof args?.figureId === 'string' ? args.figureId : ctx?.host?.figureId\n const row = await resolveFigureTarget(open ? open.trim() : undefined, domainId, ctx?.host)\n if (row) {\n base = sourceForTool(row, ctx?.host).source ?? undefined\n }\n }\n if (isV3(base) || (args?.create !== true && isV3(ctx?.host?.draftSource))) return v3ProposalUnavailable()\n\n const palette = Array.isArray(ctx?.host?.palette) ? ctx.host.palette : []\n if (palette.length === 0) {\n /*\n 팔레트를 모르면 모델이 색을 지어내고, 그 후보는 그리는 시점에 `resolveToken` 이 던진다 —\n 저장까지 되고 나서 화면에서 죽는다. 빈 목록으로 진행하지 않는다.\n */\n throw new Error('the palette is missing from the host context — the candidate would invent colours')\n }\n\n const { proposeFigure } = getDeps()\n const feedback = feedbackForTool(ctx?.host, base ? figureIdFrom(args, ctx?.host) : undefined)\n\n let hostImage: { data: Buffer; mediaType: any } | undefined\n const rawImage = ctx?.host?.image || (Array.isArray(ctx?.host?.attachments) ? ctx.host.attachments.find((a: any) => a?.mediaType?.startsWith('image/')) : undefined)\n if (rawImage?.data) {\n const rawData = typeof rawImage.data === 'string' ? rawImage.data.replace(/^data:image\\/[a-zA-Z]+;base64,/, '') : rawImage.data\n const buf = typeof rawData === 'string' ? Buffer.from(rawData, 'base64') : Buffer.from(rawData)\n hostImage = { data: buf, mediaType: rawImage.mediaType || 'image/png' }\n }\n\n let result: any\n try {\n result = await proposeFigure({\n prompt,\n palette,\n base,\n type: typeof args?.type === 'string' ? args.type : undefined,\n feedback,\n refine: !!feedback?.length,\n image: hostImage\n })\n } catch (error) {\n /*\n * 후보 생성은 세 번의 JSON·형식 검사 끝에 실패할 수 있다. 그 마지막 검사 사유를\n * ProposeFailure.reasons 에만 두면 agentic-loop은 \"만들지 못했습니다\"만 받아\n * 사용자 화면에는 \"응답이 비어있습니다\"가 된다. 재시도할 수 있는 말로 경계를\n * 넘긴다. 다른 예외(권한·통신)는 원문을 보존한다.\n */\n const reasons = Array.isArray((error as any)?.reasons) ? (error as any).reasons : []\n if (reasons.length > 0) throw new Error([(error as Error).message, ...reasons].join('\\n'))\n throw error\n }\n\n /*\n **`proposed: true` 가 카드를 만든다.** 이 표시가 없으면 후보는 도구 추적 안에 묻히고,\n 화면에는 아무것도 생기지 않는다 — 그러면 모델이 「후보를 만들었습니다」라고 쓴 것이\n 거짓말이 된다(사용자가 없는 단추를 찾는다).\n\n `label`·`reason` 이 카드에 그려지는 두 줄이고, `args` 는 같은 제안을 두 번 세지 않기\n 위한 열쇠의 일부다 — 같은 요청을 두 번 내면 카드가 하나로 접힌다.\n */\n return {\n proposed: true,\n label: base ? `${result.source.type} 고치기` : `${result.source.type} 만들기`,\n reason:\n `등급 ${result.score.grade} · 삼각형 ${result.cost.triangles} · ` +\n `한 도면 ${result.cost.at.drawCalls}회 · ${result.attempts}번에 만들었다`,\n choices: [\n { id: 'save', action: 'save', label: '초안 저장', icon: 'save', primary: true },\n { id: 'repropose', action: 'prompt', label: '다른 콘셉트 재제안', icon: 'refresh', prompt: '현재 제안과 완전히 다른 스타일과 콘셉트로 새로 제안해줘' },\n { id: 'slim', action: 'prompt', label: '비율 슬림 조정', icon: 'aspect_ratio', prompt: '전체적인 가로세로 비율을 좀 더 슬림하고 날렵하게 조정해줘' },\n { id: 'palette', action: 'prompt', label: '색상 테마 변경', icon: 'palette', prompt: '다른 팔레트 색상 테마로 부품 색상을 변경해줘' },\n { id: 'detail', action: 'prompt', label: '디테일 보강', icon: 'extension', prompt: '주요 디테일과 센서 부품을 좀 더 풍부하게 보강해줘' }\n ],\n args: {\n baseSource: base ? JSON.stringify(base) : undefined,\n prompt, figureId: base ? figureIdFrom(args, ctx?.host) : undefined,\n draftType: base && ctx?.host?.editorMode === 'new' && !figureIdFrom(args, ctx?.host)\n ? (base as any).type : undefined\n },\n source: result.source,\n score: result.score,\n cost: result.cost,\n quality: result.quality,\n violations: result.violations,\n attempts: result.attempts\n }\n }\n }\n ] as ToolSpec[]\n}\n\n/**\n * 등록 — 부팅 시 1회.\n *\n * 범주가 하나다. twin 은 관찰과 생성을 둘로 갈랐는데(운영 협의에 생성 도구가 들려 있으면 대화의\n * 성격이 흐려진다), figure 의 대화면은 저작면 하나뿐이고 그 자리에서 읽기와 후보 요청이 함께 필요하다.\n * 대화면이 늘면 그때 가른다.\n */\nexport function registerFigureToolCategories(): void {\n const authoring: ToolCategory = {\n name: 'figure-authoring',\n description: '저작 중인 Figure 의 정본·비용·발행 판정 조회와 후보 제안(저장 아님).',\n guidance: [\n '- **도형을 만들거나 고쳐 달라는 요청에는 반드시 `proposeFigureCandidate` 를 호출한다.**',\n ' 후보는 도구 호출 결과에서만 생긴다.',\n ' 호출 없이 \"후보를 만들었습니다\" · \"적용 버튼을 누르세요\" 라고 쓰는 것은 **거짓이다** — 화면에 후보 칸이 생기지 않으므로 사용자는 존재하지 않는 버튼을 찾는다.',\n ' 도크에서는 후보 카드의 버튼을 눌러야 모델러 비교 화면이 열린다. 아직 열지 않은 비교 화면이 이미 보인다고 말하지 않는다.',\n ' 요청한 부품만 바뀌었다고 추측하지 않는다. 후보와 원본을 비교한 근거가 없으면 변경 범위는 비교 화면에서 확인하도록 안내한다.',\n '- 정본을 직접 저장하지 않는다. 후보만 내고, 받을지 버릴지는 저작자가 화면에서 정한다.',\n '- 좌표와 크기는 X/Y/Z 축 이름으로 정확하게 표시한다. 확인되지 않은 가로·세로·높이 의미를 임의로 붙이지 않는다.',\n '- **고치라는 요청이면 먼저 `getFigureSource` 를 부른다.** 읽지 않고 고치면 짐작을 상대로 후보를 내게 되고, 부품 이름이 갈려 이미 배치된 보드의 바인딩이 끊긴다.',\n '- 목록 화면에는 열린 도형이 없다. 기존 도형을 조회·수정할 때는 `listFigures`에서 사용자가 말한 이름·타입의 id를 찾는다. 대상이 모호할 때만 어느 도형인지 묻는다. 새 제작 요청에는 기존 정본을 조회하지 않고 `proposeFigureCandidate`를 create: true로 호출한다.',\n '- 타입 이름을 지어내지 않는다 — `listFigures` 로 확인한다. **타입 이름은 저장되는 식별자**이고 보드가 그 이름으로 도형을 찾는다.',\n '- 발행이 안 되는 이유를 물으면 `inspectFigure` 를 부른다. 판정 없이 \"발행할 수 있습니다\"라고 말하지 않는다.',\n ' 판정의 각 항목은 **왜 문제인지와 무엇을 바꾸면 되는지**를 함께 담고 있다. 그대로 전한다 — 요약하면 저작자가 어느 부품의 어느 축을 고칠지 다시 찾아야 한다.',\n '- 무게·삼각형 수를 물으면 `getFigureSource` 의 `cost` 를 쓴다. 눈대중으로 세지 않는다.'\n ].join('\\n'),\n specs: figureToolSpecs()\n }\n\n registerToolCategory(authoring)\n}\n"]}