@tekmidian/pai 0.39.1 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -0
- package/dist/{auto-route-BlWOWT4z.mjs → auto-route-BwzmlW3W.mjs} +2 -2
- package/dist/{auto-route-BlWOWT4z.mjs.map → auto-route-BwzmlW3W.mjs.map} +1 -1
- package/dist/{chain-CqlgL7uH.mjs → chain-DhVHVnmT.mjs} +263 -821
- package/dist/chain-DhVHVnmT.mjs.map +1 -0
- package/dist/cli/index.mjs +10 -8
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/program.mjs +10 -8
- package/dist/{clusters-wlK0w41E.mjs → clusters-DRHhfli9.mjs} +1 -1
- package/dist/{clusters-wlK0w41E.mjs.map → clusters-DRHhfli9.mjs.map} +1 -1
- package/dist/{config-B64vFg14.mjs → config-D1G9IFpn.mjs} +50 -3
- package/dist/config-D1G9IFpn.mjs.map +1 -0
- package/dist/{config-C_ErGddD.mjs → config-DD52BPB-.mjs} +1 -1
- package/dist/config-Dl8lT4Lu.mjs +527 -0
- package/dist/config-Dl8lT4Lu.mjs.map +1 -0
- package/dist/{context-handover-cache-BpUojjsi.mjs → context-handover-cache-C6_rs7JT.mjs} +112 -24
- package/dist/context-handover-cache-C6_rs7JT.mjs.map +1 -0
- package/dist/daemon/index.mjs +12 -10
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/daemon-BSL9sXxm.mjs +22 -0
- package/dist/{daemon-CTkN_Vn8.mjs → daemon-CpdVUnl_.mjs} +26 -26
- package/dist/{daemon-CTkN_Vn8.mjs.map → daemon-CpdVUnl_.mjs.map} +1 -1
- package/dist/daemon-mcp/index.mjs +76 -6
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/detector-BLVOCiJ_.mjs +5 -0
- package/dist/{detector-Bwk_4Pk4.mjs → detector-smpCm7ku.mjs} +1 -1
- package/dist/{detector-Bwk_4Pk4.mjs.map → detector-smpCm7ku.mjs.map} +1 -1
- package/dist/{factory-A9x-T2Kg.mjs → factory-Bc5EyN54.mjs} +7 -7
- package/dist/{factory-A9x-T2Kg.mjs.map → factory-Bc5EyN54.mjs.map} +1 -1
- package/dist/factory-C7ua_3ik.mjs +3 -0
- package/dist/{fallback-DqYhiACU.mjs → fallback-CdXv-Np0.mjs} +139 -49
- package/dist/fallback-CdXv-Np0.mjs.map +1 -0
- package/dist/hooks/context-compression-hook.mjs +136 -22
- package/dist/hooks/context-compression-hook.mjs.map +4 -4
- package/dist/hooks/post-compact-inject.mjs +98 -7
- package/dist/hooks/post-compact-inject.mjs.map +4 -4
- package/dist/hooks/route-agents-to-worker.mjs +36 -2
- package/dist/hooks/route-agents-to-worker.mjs.map +2 -2
- package/dist/hooks/worker-proxy.mjs +36 -2
- package/dist/hooks/worker-proxy.mjs.map +2 -2
- package/dist/hooks/worker-status-line.mjs +110 -86
- package/dist/hooks/worker-status-line.mjs.map +4 -4
- package/dist/{indexer-backend-TG64CCQC.mjs → indexer-backend-rohVVKrA.mjs} +1 -1
- package/dist/{indexer-backend-TG64CCQC.mjs.map → indexer-backend-rohVVKrA.mjs.map} +1 -1
- package/dist/{ipc-client-BmypMNYk.mjs → ipc-client-C7AVpsyv.mjs} +2 -2
- package/dist/{ipc-client-BmypMNYk.mjs.map → ipc-client-C7AVpsyv.mjs.map} +1 -1
- package/dist/{kg-entity-r8duqhi9.mjs → kg-entity-DbOMPdF9.mjs} +1 -1
- package/dist/{kg-entity-r8duqhi9.mjs.map → kg-entity-DbOMPdF9.mjs.map} +1 -1
- package/dist/{latent-ideas-B7wq75Pt.mjs → latent-ideas-iNp5s55R.mjs} +2 -2
- package/dist/{latent-ideas-B7wq75Pt.mjs.map → latent-ideas-iNp5s55R.mjs.map} +1 -1
- package/dist/{link-boost-HkG7JWZR.mjs → link-boost-NNzADHX_.mjs} +1 -1
- package/dist/{link-boost-HkG7JWZR.mjs.map → link-boost-NNzADHX_.mjs.map} +1 -1
- package/dist/{main-resolver-DlaLOFBA.mjs → main-resolver-CM1IHbuu.mjs} +78 -75
- package/dist/main-resolver-CM1IHbuu.mjs.map +1 -0
- package/dist/main-resolver-E5WlilhM.mjs +6 -0
- package/dist/{neighborhood-lThN-MaQ.mjs → neighborhood-AGL7oXQ2.mjs} +1 -1
- package/dist/{neighborhood-lThN-MaQ.mjs.map → neighborhood-AGL7oXQ2.mjs.map} +1 -1
- package/dist/{note-context-b6k0mAKi.mjs → note-context-BHwYpSFf.mjs} +1 -1
- package/dist/{note-context-b6k0mAKi.mjs.map → note-context-BHwYpSFf.mjs.map} +1 -1
- package/dist/{planner-DGV8VFib.mjs → planner-Cm3g6fWH.mjs} +5 -3
- package/dist/{planner-DGV8VFib.mjs.map → planner-Cm3g6fWH.mjs.map} +1 -1
- package/dist/{postgres-mW1n7Vi1.mjs → postgres-Dwxe290H.mjs} +1 -1
- package/dist/{postgres-mW1n7Vi1.mjs.map → postgres-Dwxe290H.mjs.map} +1 -1
- package/dist/{program-B-QJXCRb.mjs → program-AR0qlYRg.mjs} +309 -78
- package/dist/program-AR0qlYRg.mjs.map +1 -0
- package/dist/query-feedback-CGcEiGtY.mjs +3 -0
- package/dist/{query-feedback-BV4CcxqS.mjs → query-feedback-DZfjI34w.mjs} +1 -1
- package/dist/{query-feedback-BV4CcxqS.mjs.map → query-feedback-DZfjI34w.mjs.map} +1 -1
- package/dist/router-CAtyVQud.mjs +3 -0
- package/dist/{router-DcHKnEPa.mjs → router-DHracTUC.mjs} +1 -1
- package/dist/{router-DcHKnEPa.mjs.map → router-DHracTUC.mjs.map} +1 -1
- package/dist/server-BOuAOj9b.mjs +386 -0
- package/dist/server-BOuAOj9b.mjs.map +1 -0
- package/dist/skills/Worker/SKILL.md +6 -1
- package/dist/{sources-kLnQsNrW.mjs → sources-ju6AA7lL.mjs} +1 -1
- package/dist/{sources-kLnQsNrW.mjs.map → sources-ju6AA7lL.mjs.map} +1 -1
- package/dist/{sqlite-BenGr3UP.mjs → sqlite-kFZeWCJ2.mjs} +1 -1
- package/dist/{sqlite-BenGr3UP.mjs.map → sqlite-kFZeWCJ2.mjs.map} +1 -1
- package/dist/{state-Ca9F_sZc.mjs → state-DH_mp5Xe.mjs} +1 -1
- package/dist/{state-CAeyOdfq.mjs → state-ahFAbD8b.mjs} +1 -1
- package/dist/{state-CAeyOdfq.mjs.map → state-ahFAbD8b.mjs.map} +1 -1
- package/dist/{themes-BI4GMRP9.mjs → themes-DqhNUUQk.mjs} +1 -1
- package/dist/{themes-BI4GMRP9.mjs.map → themes-DqhNUUQk.mjs.map} +1 -1
- package/dist/{tools-DGcB3o_S.mjs → tools-CMlHa9lZ.mjs} +14 -14
- package/dist/{tools-DGcB3o_S.mjs.map → tools-CMlHa9lZ.mjs.map} +1 -1
- package/dist/{tools-Bp7hj6OW.mjs → tools-COna9vZR.mjs} +1 -1
- package/dist/{trace-bobARFEX.mjs → trace-DTP6k4B_.mjs} +1 -1
- package/dist/{trace-bobARFEX.mjs.map → trace-DTP6k4B_.mjs.map} +1 -1
- package/dist/{vault-indexer-C3OfXTNF.mjs → vault-indexer-YMH22mLU.mjs} +1 -1
- package/dist/{vault-indexer-C3OfXTNF.mjs.map → vault-indexer-YMH22mLU.mjs.map} +1 -1
- package/dist/{work-queue-worker-R7UGIag_.mjs → work-queue-worker-B3nl8yyn.mjs} +23 -26
- package/dist/work-queue-worker-B3nl8yyn.mjs.map +1 -0
- package/dist/work-queue-worker-BM1GcAAq.mjs +14 -0
- package/dist/{zettelkasten-m5QPtb-L.mjs → zettelkasten-DDvKRm2V.mjs} +3 -3
- package/dist/{zettelkasten-m5QPtb-L.mjs.map → zettelkasten-DDvKRm2V.mjs.map} +1 -1
- package/docs/commands/README.md +4 -2
- package/docs/commands/worker.md +48 -5
- package/docs/images/provider-abstraction-live.png +0 -0
- package/docs/images/workers.png +0 -0
- package/docs/launcher-independence.md +70 -0
- package/docs/provider-abstraction.md +87 -0
- package/docs/provider-independence-details.md +330 -0
- package/docs/provider-independence.md +75 -0
- package/docs/worker.md +29 -11
- package/package.json +1 -1
- package/scripts/build-hooks.mjs +6 -1
- package/scripts/build-skill-stubs.mjs +17 -4
- package/src/hooks/ts/lib/context-fill.test.ts +158 -4
- package/src/hooks/ts/lib/context-fill.ts +102 -35
- package/statusline-command.sh +120 -21
- package/dist/chain-CqlgL7uH.mjs.map +0 -1
- package/dist/config-B64vFg14.mjs.map +0 -1
- package/dist/context-handover-cache-BpUojjsi.mjs.map +0 -1
- package/dist/daemon-CmHHmKpg.mjs +0 -20
- package/dist/detector-DO730Zq0.mjs +0 -5
- package/dist/factory-BD-np0Vd.mjs +0 -3
- package/dist/fallback-DqYhiACU.mjs.map +0 -1
- package/dist/main-resolver-D6IImXvF.mjs +0 -4
- package/dist/main-resolver-DlaLOFBA.mjs.map +0 -1
- package/dist/program-B-QJXCRb.mjs.map +0 -1
- package/dist/query-feedback-DSVyHtrG.mjs +0 -3
- package/dist/router-Bk77E7hj.mjs +0 -3
- package/dist/runtime-paths-rni52zHX.mjs +0 -50
- package/dist/runtime-paths-rni52zHX.mjs.map +0 -1
- package/dist/work-queue-worker-DW8lz-Oo.mjs +0 -11
- package/dist/work-queue-worker-R7UGIag_.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -82,6 +82,47 @@ Browse the same pages on GitHub under [`docs/commands/`](docs/commands/README.md
|
|
|
82
82
|
|
|
83
83
|
---
|
|
84
84
|
|
|
85
|
+
## Worker Providers — Run the Fleet Anywhere
|
|
86
|
+
|
|
87
|
+
**Read the story: [Provider Independence — how I freed my stack from a single vendor in one day](docs/provider-independence.md).**
|
|
88
|
+
|
|
89
|
+
Only the outer orchestrator session runs on Anthropic. Every worker PAI spawns — research, drafting, implementation, review, spotchecks — runs on a managed provider you choose. The same provider layer carries the daemon's background calls and the session picker, so the whole stack moves together.
|
|
90
|
+
|
|
91
|
+
### Why
|
|
92
|
+
|
|
93
|
+
- **Vendor independence.** Any provider that speaks the Anthropic Messages protocol is a registry entry: models, key file, price tier. OpenAI-protocol providers work through a built-in translating proxy. Switching is configuration, not surgery.
|
|
94
|
+
- **Cost control.** Parallel work is a commodity; it should not burn your premium seat. Workers bill against their own provider, and cheap classes resolve to the provider's fast model automatically.
|
|
95
|
+
- **No lock-in to one orchestrator vendor.** Sessions run on the active provider too — the picker launches through it, and `pai worker fallback` extends that machine-wide.
|
|
96
|
+
- **Survives orchestrator outages.** Workers carry their own provider credentials, so a quota freeze or outage on the vendor seat does not stop delegated work.
|
|
97
|
+
|
|
98
|
+
### How
|
|
99
|
+
|
|
100
|
+
- **Managed providers.** `pai worker providers add` registers one, `pai worker providers use <name>` switches the fleet, `pai worker off` disables routing entirely (the Agent tool runs on Anthropic again), `pai worker on` re-enables it.
|
|
101
|
+
- **Classes route work to the right model.** `--class` picks the provider and model for the job: `draft`, `plan`, `implement`, `review`, `research`, `spotcheck`, `simple`, `complex`, `image`. `pai worker classes` shows and edits the mapping; `--provider` / `--model` override for a single run.
|
|
102
|
+
- **Every worker spawn stands alone.** The orchestrator's API key is stripped and the spawn gets the provider's base URL, token and model ids instead — proven live: a worker answers with the parent's credentials gone. No inherited billing, no fallback to the vendor login.
|
|
103
|
+
|
|
104
|
+
### What
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
pai worker run -p '<task>' --class implement # one worker on a provider
|
|
108
|
+
pai worker ps # this session's workers (--all: every one)
|
|
109
|
+
pai worker follow <id> # live transcript of one worker
|
|
110
|
+
pai worker pane # shared follow pane for the session
|
|
111
|
+
pai worker replay <id> # transcript of a finished or running worker
|
|
112
|
+
pai worker say <id> <text> # message a running worker mid-run
|
|
113
|
+
pai worker handoff '<json>' # from inside a worker: report to the parent
|
|
114
|
+
pai worker merge <id> # merge the worker's branch back, drop the worktree
|
|
115
|
+
pai worker watch # ps refreshed every 2 seconds
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The rest of the surface — `discard`, `resume`, `controls`, `proxy`, `mcp`, `model`, `providers`, `classes` — is in `pai help worker` and [docs/commands/worker.md](docs/commands/worker.md).
|
|
119
|
+
|
|
120
|
+

|
|
121
|
+
|
|
122
|
+
Get started in three copy-paste steps: **[docs/provider-independence.md](docs/provider-independence.md)**. For the depth — provider registry, statusline instrumentation, seam patches, current limits — see **[docs/provider-abstraction.md](docs/provider-abstraction.md)**.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
85
126
|
## Automatic Session Notes — by Topic
|
|
86
127
|
|
|
87
128
|
PAI's headline feature: **every session is automatically documented.** No manual note-taking, no "pause session" commands, no forgetting to save what you did.
|
|
@@ -26,7 +26,7 @@ async function autoRoute(registryDb, federation, cwd, context) {
|
|
|
26
26
|
const markerResult = findMarkerUpward(registryDb, target);
|
|
27
27
|
if (markerResult) return markerResult;
|
|
28
28
|
if (context && context.trim().length > 0) {
|
|
29
|
-
const { detectTopicShift } = await import("./detector-
|
|
29
|
+
const { detectTopicShift } = await import("./detector-BLVOCiJ_.mjs");
|
|
30
30
|
const topicResult = await detectTopicShift(registryDb, federation, {
|
|
31
31
|
context,
|
|
32
32
|
threshold: .5
|
|
@@ -83,4 +83,4 @@ function formatAutoRouteJson(result) {
|
|
|
83
83
|
|
|
84
84
|
//#endregion
|
|
85
85
|
export { autoRoute, formatAutoRouteJson };
|
|
86
|
-
//# sourceMappingURL=auto-route-
|
|
86
|
+
//# sourceMappingURL=auto-route-BwzmlW3W.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-route-
|
|
1
|
+
{"version":3,"file":"auto-route-BwzmlW3W.mjs","names":[],"sources":["../src/session/auto-route.ts"],"sourcesContent":["/**\n * Auto-route: automatic project routing suggestion on session start.\n *\n * Given a working directory (and optional conversation context), determine\n * which registered project the session belongs to.\n *\n * Strategy (in priority order):\n * 1. Path match — exact or parent-directory match in the project registry\n * 2. Marker walk — walk up from cwd looking for Notes/PAI.md, resolve slug\n * 3. Topic match — BM25 keyword search against memory (requires context text)\n *\n * The function is stateless and works with direct DB access (no daemon\n * required), making it fast and safe to call during session startup.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport type { StorageBackend } from \"../storage/interface.js\";\nimport { resolve, dirname } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { readPaiMarker } from \"../registry/pai-marker.js\";\nimport { detectProject } from \"../cli/commands/detect.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type AutoRouteMethod = \"path\" | \"marker\" | \"topic\";\n\nexport interface AutoRouteResult {\n /** Project slug */\n slug: string;\n /** Human-readable project name */\n display_name: string;\n /** Absolute path to the project root */\n root_path: string;\n /** How the project was detected */\n method: AutoRouteMethod;\n /** Confidence [0,1]: 1.0 for path/marker matches, BM25 fraction for topic */\n confidence: number;\n}\n\n// ---------------------------------------------------------------------------\n// Core function\n// ---------------------------------------------------------------------------\n\n/**\n * Determine which project a session should be routed to.\n *\n * @param registryDb Open PAI registry database\n * @param federation Memory storage backend (needed only for topic fallback)\n * @param cwd Working directory to detect from (defaults to process.cwd())\n * @param context Optional conversation text for topic-based fallback\n * @returns Best project match, or null if nothing matched\n */\nexport async function autoRoute(\n registryDb: Database,\n federation: Database | StorageBackend,\n cwd?: string,\n context?: string\n): Promise<AutoRouteResult | null> {\n const target = resolve(cwd ?? process.cwd());\n\n // -------------------------------------------------------------------------\n // Strategy 1: Path match via registry\n // -------------------------------------------------------------------------\n\n const pathMatch = detectProject(registryDb, target);\n\n if (pathMatch) {\n return {\n slug: pathMatch.slug,\n display_name: pathMatch.display_name,\n root_path: pathMatch.root_path,\n method: \"path\",\n confidence: 1.0,\n };\n }\n\n // -------------------------------------------------------------------------\n // Strategy 2: PAI.md marker file walk\n //\n // Walk up from cwd, checking <dir>/Notes/PAI.md at each level.\n // Once found, resolve the slug against the registry to get full project info.\n // -------------------------------------------------------------------------\n\n const markerResult = findMarkerUpward(registryDb, target);\n if (markerResult) {\n return markerResult;\n }\n\n // -------------------------------------------------------------------------\n // Strategy 3: Topic detection (requires context text)\n // -------------------------------------------------------------------------\n\n if (context && context.trim().length > 0) {\n // Lazy import to avoid bundler pulling in daemon/index.mjs at module load time\n const { detectTopicShift } = await import(\"../topics/detector.js\");\n const topicResult = await detectTopicShift(registryDb, federation, {\n context,\n threshold: 0.5, // Lower threshold for initial routing (vs shift detection)\n });\n\n if (topicResult.suggestedProject && topicResult.confidence > 0) {\n // Look up the full project info from the registry\n const projectRow = registryDb\n .prepare(\n \"SELECT slug, display_name, root_path FROM projects WHERE slug = ? AND status != 'archived'\"\n )\n .get(topicResult.suggestedProject) as\n | { slug: string; display_name: string; root_path: string }\n | undefined;\n\n if (projectRow) {\n return {\n slug: projectRow.slug,\n display_name: projectRow.display_name,\n root_path: projectRow.root_path,\n method: \"topic\",\n confidence: topicResult.confidence,\n };\n }\n }\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Marker walk helper\n// ---------------------------------------------------------------------------\n\n/**\n * Walk up the directory tree from `startDir`, checking each level for a\n * `Notes/PAI.md` file. If found, read the slug and look up the project.\n *\n * Stops at the filesystem root or after 20 levels (safety guard).\n */\nfunction findMarkerUpward(\n registryDb: Database,\n startDir: string\n): AutoRouteResult | null {\n let current = startDir;\n let depth = 0;\n\n while (depth < 20) {\n const markerPath = `${current}/Notes/PAI.md`;\n\n if (existsSync(markerPath)) {\n const marker = readPaiMarker(current);\n\n if (marker && marker.status !== \"archived\") {\n // Resolve slug to full project info in the registry\n const projectRow = registryDb\n .prepare(\n \"SELECT slug, display_name, root_path FROM projects WHERE slug = ? AND status != 'archived'\"\n )\n .get(marker.slug) as\n | { slug: string; display_name: string; root_path: string }\n | undefined;\n\n if (projectRow) {\n return {\n slug: projectRow.slug,\n display_name: projectRow.display_name,\n root_path: projectRow.root_path,\n method: \"marker\",\n confidence: 1.0,\n };\n }\n }\n }\n\n const parent = dirname(current);\n if (parent === current) break; // Reached filesystem root\n current = parent;\n depth++;\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Format helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Format an AutoRouteResult as a human-readable string for CLI output.\n */\nexport function formatAutoRoute(result: AutoRouteResult): string {\n const lines: string[] = [\n `slug: ${result.slug}`,\n `display_name: ${result.display_name}`,\n `root_path: ${result.root_path}`,\n `method: ${result.method}`,\n `confidence: ${(result.confidence * 100).toFixed(0)}%`,\n ];\n return lines.join(\"\\n\");\n}\n\n/**\n * Format an AutoRouteResult as JSON for machine consumption.\n */\nexport function formatAutoRouteJson(result: AutoRouteResult): string {\n return JSON.stringify(result, null, 2);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAsDA,eAAsB,UACpB,YACA,YACA,KACA,SACiC;CACjC,MAAM,SAAS,QAAQ,OAAO,QAAQ,KAAK,CAAC;CAM5C,MAAM,YAAY,cAAc,YAAY,OAAO;AAEnD,KAAI,UACF,QAAO;EACL,MAAM,UAAU;EAChB,cAAc,UAAU;EACxB,WAAW,UAAU;EACrB,QAAQ;EACR,YAAY;EACb;CAUH,MAAM,eAAe,iBAAiB,YAAY,OAAO;AACzD,KAAI,aACF,QAAO;AAOT,KAAI,WAAW,QAAQ,MAAM,CAAC,SAAS,GAAG;EAExC,MAAM,EAAE,qBAAqB,MAAM,OAAO;EAC1C,MAAM,cAAc,MAAM,iBAAiB,YAAY,YAAY;GACjE;GACA,WAAW;GACZ,CAAC;AAEF,MAAI,YAAY,oBAAoB,YAAY,aAAa,GAAG;GAE9D,MAAM,aAAa,WAChB,QACC,6FACD,CACA,IAAI,YAAY,iBAAiB;AAIpC,OAAI,WACF,QAAO;IACL,MAAM,WAAW;IACjB,cAAc,WAAW;IACzB,WAAW,WAAW;IACtB,QAAQ;IACR,YAAY,YAAY;IACzB;;;AAKP,QAAO;;;;;;;;AAaT,SAAS,iBACP,YACA,UACwB;CACxB,IAAI,UAAU;CACd,IAAI,QAAQ;AAEZ,QAAO,QAAQ,IAAI;AAGjB,MAAI,WAFe,GAAG,QAAQ,eAEJ,EAAE;GAC1B,MAAM,SAAS,cAAc,QAAQ;AAErC,OAAI,UAAU,OAAO,WAAW,YAAY;IAE1C,MAAM,aAAa,WAChB,QACC,6FACD,CACA,IAAI,OAAO,KAAK;AAInB,QAAI,WACF,QAAO;KACL,MAAM,WAAW;KACjB,cAAc,WAAW;KACzB,WAAW,WAAW;KACtB,QAAQ;KACR,YAAY;KACb;;;EAKP,MAAM,SAAS,QAAQ,QAAQ;AAC/B,MAAI,WAAW,QAAS;AACxB,YAAU;AACV;;AAGF,QAAO;;;;;AAwBT,SAAgB,oBAAoB,QAAiC;AACnE,QAAO,KAAK,UAAU,QAAQ,MAAM,EAAE"}
|