@nanonets/graft 0.23.0 → 0.24.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 +4 -4
- package/dist/ai/crux.d.ts +31 -3
- package/dist/ai/crux.d.ts.map +1 -1
- package/dist/ai/crux.js +88 -10
- package/dist/ai/crux.js.map +1 -1
- package/dist/ai/llm/openai.d.ts +101 -0
- package/dist/ai/llm/openai.d.ts.map +1 -1
- package/dist/ai/llm/openai.js +396 -6
- package/dist/ai/llm/openai.js.map +1 -1
- package/dist/ai/llm/recover-tool.d.ts +2 -0
- package/dist/ai/llm/recover-tool.d.ts.map +1 -1
- package/dist/ai/llm/recover-tool.js +1 -1
- package/dist/ai/llm/recover-tool.js.map +1 -1
- package/dist/ai/synthesize.d.ts.map +1 -1
- package/dist/ai/synthesize.js +100 -1
- package/dist/ai/synthesize.js.map +1 -1
- package/dist/claude/format.d.ts +21 -0
- package/dist/claude/format.d.ts.map +1 -1
- package/dist/claude/format.js +46 -1
- package/dist/claude/format.js.map +1 -1
- package/dist/claude/hooks.d.ts +5 -9
- package/dist/claude/hooks.d.ts.map +1 -1
- package/dist/claude/hooks.js +71 -58
- package/dist/claude/hooks.js.map +1 -1
- package/dist/claude/init.js +3 -3
- package/dist/claude/init.js.map +1 -1
- package/dist/claude/note-signals.d.ts +28 -0
- package/dist/claude/note-signals.d.ts.map +1 -0
- package/dist/claude/note-signals.js +325 -0
- package/dist/claude/note-signals.js.map +1 -0
- package/dist/claude/shim-template.d.ts.map +1 -1
- package/dist/claude/shim-template.js +17 -6
- package/dist/claude/shim-template.js.map +1 -1
- package/dist/claude/skill-template.d.ts.map +1 -1
- package/dist/claude/skill-template.js +49 -18
- package/dist/claude/skill-template.js.map +1 -1
- package/dist/claude/state.d.ts +4 -3
- package/dist/claude/state.d.ts.map +1 -1
- package/dist/claude/state.js.map +1 -1
- package/dist/claude/sync-run.js +1 -1
- package/dist/claude/sync-run.js.map +1 -1
- package/dist/cli-picker.d.ts +12 -5
- package/dist/cli-picker.d.ts.map +1 -1
- package/dist/cli-picker.js +24 -8
- package/dist/cli-picker.js.map +1 -1
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +149 -43
- package/dist/cli.js.map +1 -1
- package/dist/context/batches.d.ts +29 -0
- package/dist/context/batches.d.ts.map +1 -0
- package/dist/context/batches.js +130 -0
- package/dist/context/batches.js.map +1 -0
- package/dist/context/build.d.ts +7 -2
- package/dist/context/build.d.ts.map +1 -1
- package/dist/context/build.js +84 -50
- package/dist/context/build.js.map +1 -1
- package/dist/context/node-file.d.ts.map +1 -1
- package/dist/context/node-file.js +41 -4
- package/dist/context/node-file.js.map +1 -1
- package/dist/engine.d.ts +2 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +1 -0
- package/dist/engine.js.map +1 -1
- package/dist/graph/cards.d.ts.map +1 -1
- package/dist/graph/cards.js +9 -4
- package/dist/graph/cards.js.map +1 -1
- package/dist/graph/enrich.d.ts.map +1 -1
- package/dist/graph/enrich.js +8 -3
- package/dist/graph/enrich.js.map +1 -1
- package/dist/graph/generic.d.ts.map +1 -1
- package/dist/graph/generic.js +207 -6
- package/dist/graph/generic.js.map +1 -1
- package/dist/graph/lsp/client.d.ts +18 -0
- package/dist/graph/lsp/client.d.ts.map +1 -1
- package/dist/graph/lsp/client.js +40 -2
- package/dist/graph/lsp/client.js.map +1 -1
- package/dist/graph/lsp/enrich.d.ts.map +1 -1
- package/dist/graph/lsp/enrich.js +9 -1
- package/dist/graph/lsp/enrich.js.map +1 -1
- package/dist/graph/lsp/registry.d.ts +7 -0
- package/dist/graph/lsp/registry.d.ts.map +1 -1
- package/dist/graph/lsp/registry.js +27 -2
- package/dist/graph/lsp/registry.js.map +1 -1
- package/dist/graph/queries/gleam.scm +51 -0
- package/dist/graph/resolve.d.ts.map +1 -1
- package/dist/graph/resolve.js +65 -3
- package/dist/graph/resolve.js.map +1 -1
- package/dist/graph/workspace-cli.d.ts +2 -0
- package/dist/graph/workspace-cli.d.ts.map +1 -1
- package/dist/graph/workspace-cli.js +1 -1
- package/dist/graph/workspace-cli.js.map +1 -1
- package/dist/init-guard.d.ts +11 -0
- package/dist/init-guard.d.ts.map +1 -0
- package/dist/init-guard.js +21 -0
- package/dist/init-guard.js.map +1 -0
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +2 -1
- package/dist/mcp/tools.js.map +1 -1
- package/dist/notes/branches.d.ts +7 -1
- package/dist/notes/branches.d.ts.map +1 -1
- package/dist/notes/branches.js +31 -3
- package/dist/notes/branches.js.map +1 -1
- package/dist/notes/cold-start.d.ts +12 -2
- package/dist/notes/cold-start.d.ts.map +1 -1
- package/dist/notes/cold-start.js +46 -5
- package/dist/notes/cold-start.js.map +1 -1
- package/dist/notes/credit.d.ts +37 -0
- package/dist/notes/credit.d.ts.map +1 -0
- package/dist/notes/credit.js +78 -0
- package/dist/notes/credit.js.map +1 -0
- package/dist/notes/find-notes.d.ts +17 -0
- package/dist/notes/find-notes.d.ts.map +1 -0
- package/dist/notes/find-notes.js +48 -0
- package/dist/notes/find-notes.js.map +1 -0
- package/dist/notes/flatten.d.ts +31 -0
- package/dist/notes/flatten.d.ts.map +1 -0
- package/dist/notes/flatten.js +73 -0
- package/dist/notes/flatten.js.map +1 -0
- package/dist/notes/git-facts.d.ts +7 -1
- package/dist/notes/git-facts.d.ts.map +1 -1
- package/dist/notes/git-facts.js +12 -2
- package/dist/notes/git-facts.js.map +1 -1
- package/dist/notes/home.d.ts +24 -6
- package/dist/notes/home.d.ts.map +1 -1
- package/dist/notes/home.js +82 -15
- package/dist/notes/home.js.map +1 -1
- package/dist/notes/learning-fields.d.ts +102 -0
- package/dist/notes/learning-fields.d.ts.map +1 -0
- package/dist/notes/learning-fields.js +214 -0
- package/dist/notes/learning-fields.js.map +1 -0
- package/dist/notes/memory-notes.d.ts +71 -0
- package/dist/notes/memory-notes.d.ts.map +1 -0
- package/dist/notes/memory-notes.js +239 -0
- package/dist/notes/memory-notes.js.map +1 -0
- package/dist/notes/note-gate.d.ts +42 -0
- package/dist/notes/note-gate.d.ts.map +1 -0
- package/dist/notes/note-gate.js +181 -0
- package/dist/notes/note-gate.js.map +1 -0
- package/dist/notes/notes.d.ts +29 -77
- package/dist/notes/notes.d.ts.map +1 -1
- package/dist/notes/notes.js +69 -228
- package/dist/notes/notes.js.map +1 -1
- package/dist/notes/pr-reviews.d.ts +35 -7
- package/dist/notes/pr-reviews.d.ts.map +1 -1
- package/dist/notes/pr-reviews.js +156 -46
- package/dist/notes/pr-reviews.js.map +1 -1
- package/dist/notes/secrets.d.ts +28 -0
- package/dist/notes/secrets.d.ts.map +1 -0
- package/dist/notes/secrets.js +362 -0
- package/dist/notes/secrets.js.map +1 -0
- package/dist/notes/session-cost.d.ts +2 -0
- package/dist/notes/session-cost.d.ts.map +1 -1
- package/dist/notes/session-cost.js +8 -3
- package/dist/notes/session-cost.js.map +1 -1
- package/dist/notes/targets.d.ts +37 -0
- package/dist/notes/targets.d.ts.map +1 -0
- package/dist/notes/targets.js +108 -0
- package/dist/notes/targets.js.map +1 -0
- package/dist/notes/team-check.d.ts +22 -0
- package/dist/notes/team-check.d.ts.map +1 -0
- package/dist/notes/team-check.js +130 -0
- package/dist/notes/team-check.js.map +1 -0
- package/dist/notes/trail-home.d.ts +3 -0
- package/dist/notes/trail-home.d.ts.map +1 -0
- package/dist/notes/trail-home.js +12 -0
- package/dist/notes/trail-home.js.map +1 -0
- package/dist/search/grep-cli.d.ts +5 -2
- package/dist/search/grep-cli.d.ts.map +1 -1
- package/dist/search/grep-cli.js +11 -4
- package/dist/search/grep-cli.js.map +1 -1
- package/dist/search/ignored-code.d.ts +9 -0
- package/dist/search/ignored-code.d.ts.map +1 -0
- package/dist/search/ignored-code.js +73 -0
- package/dist/search/ignored-code.js.map +1 -0
- package/dist/skills/answers-prompt.d.ts +13 -0
- package/dist/skills/answers-prompt.d.ts.map +1 -0
- package/dist/skills/answers-prompt.js +83 -0
- package/dist/skills/answers-prompt.js.map +1 -0
- package/dist/skills/never-save.d.ts +5 -0
- package/dist/skills/never-save.d.ts.map +1 -0
- package/dist/skills/never-save.js +158 -0
- package/dist/skills/never-save.js.map +1 -0
- package/dist/skills/params.d.ts +39 -0
- package/dist/skills/params.d.ts.map +1 -0
- package/dist/skills/params.js +275 -0
- package/dist/skills/params.js.map +1 -0
- package/dist/skills/ran-less.d.ts +5 -0
- package/dist/skills/ran-less.d.ts.map +1 -0
- package/dist/skills/ran-less.js +51 -0
- package/dist/skills/ran-less.js.map +1 -0
- package/dist/skills/risk-delta.d.ts +32 -0
- package/dist/skills/risk-delta.d.ts.map +1 -0
- package/dist/skills/risk-delta.js +56 -0
- package/dist/skills/risk-delta.js.map +1 -0
- package/dist/skills/risk.d.ts +73 -0
- package/dist/skills/risk.d.ts.map +1 -0
- package/dist/skills/risk.js +396 -0
- package/dist/skills/risk.js.map +1 -0
- package/dist/skills/types.d.ts +422 -0
- package/dist/skills/types.d.ts.map +1 -0
- package/dist/skills/types.js +2 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/telemetry/contract.d.ts.map +1 -1
- package/dist/telemetry/contract.js +8 -6
- package/dist/telemetry/contract.js.map +1 -1
- package/dist/telemetry/notice.d.ts.map +1 -1
- package/dist/telemetry/notice.js +3 -1
- package/dist/telemetry/notice.js.map +1 -1
- package/dist/trace/check-reads.d.ts +36 -0
- package/dist/trace/check-reads.d.ts.map +1 -0
- package/dist/trace/check-reads.js +57 -0
- package/dist/trace/check-reads.js.map +1 -0
- package/dist/trace/host.d.ts +40 -0
- package/dist/trace/host.d.ts.map +1 -0
- package/dist/trace/host.js +84 -0
- package/dist/trace/host.js.map +1 -0
- package/dist/trace/redact.d.ts +8 -0
- package/dist/trace/redact.d.ts.map +1 -0
- package/dist/trace/redact.js +63 -0
- package/dist/trace/redact.js.map +1 -0
- package/dist/trace/seen-cache.d.ts +9 -0
- package/dist/trace/seen-cache.d.ts.map +1 -0
- package/dist/trace/seen-cache.js +46 -0
- package/dist/trace/seen-cache.js.map +1 -0
- package/dist/trace/shell-scan.d.ts +52 -0
- package/dist/trace/shell-scan.d.ts.map +1 -0
- package/dist/trace/shell-scan.js +318 -0
- package/dist/trace/shell-scan.js.map +1 -0
- package/dist/trace/shell-words.d.ts +91 -0
- package/dist/trace/shell-words.d.ts.map +1 -0
- package/dist/trace/shell-words.js +558 -0
- package/dist/trace/shell-words.js.map +1 -0
- package/dist/trace/shell.d.ts +91 -0
- package/dist/trace/shell.d.ts.map +1 -0
- package/dist/trace/shell.js +435 -0
- package/dist/trace/shell.js.map +1 -0
- package/dist/trace/state.d.ts +27 -0
- package/dist/trace/state.d.ts.map +1 -0
- package/dist/trace/state.js +98 -0
- package/dist/trace/state.js.map +1 -0
- package/dist/trace/tamper-code.d.ts +9 -0
- package/dist/trace/tamper-code.d.ts.map +1 -0
- package/dist/trace/tamper-code.js +174 -0
- package/dist/trace/tamper-code.js.map +1 -0
- package/dist/trace/tamper-git.d.ts +12 -0
- package/dist/trace/tamper-git.d.ts.map +1 -0
- package/dist/trace/tamper-git.js +77 -0
- package/dist/trace/tamper-git.js.map +1 -0
- package/dist/trace/types.d.ts +477 -0
- package/dist/trace/types.d.ts.map +1 -0
- package/dist/trace/types.js +12 -0
- package/dist/trace/types.js.map +1 -0
- package/dist/trace/walls-glob.d.ts +11 -0
- package/dist/trace/walls-glob.d.ts.map +1 -0
- package/dist/trace/walls-glob.js +264 -0
- package/dist/trace/walls-glob.js.map +1 -0
- package/dist/trace/walls-quoted.d.ts +25 -0
- package/dist/trace/walls-quoted.d.ts.map +1 -0
- package/dist/trace/walls-quoted.js +81 -0
- package/dist/trace/walls-quoted.js.map +1 -0
- package/dist/upkeep.d.ts +1 -1
- package/dist/upkeep.d.ts.map +1 -1
- package/dist/upkeep.js +4 -1
- package/dist/upkeep.js.map +1 -1
- package/dist/util/actions-folder.d.ts +2 -0
- package/dist/util/actions-folder.d.ts.map +1 -0
- package/dist/util/actions-folder.js +9 -0
- package/dist/util/actions-folder.js.map +1 -0
- package/dist/util/front-matter.d.ts +31 -4
- package/dist/util/front-matter.d.ts.map +1 -1
- package/dist/util/front-matter.js +95 -3
- package/dist/util/front-matter.js.map +1 -1
- package/dist/util/private-file.d.ts +14 -0
- package/dist/util/private-file.d.ts.map +1 -0
- package/dist/util/private-file.js +82 -0
- package/dist/util/private-file.js.map +1 -0
- package/dist/util/private-fs.d.ts +25 -0
- package/dist/util/private-fs.d.ts.map +1 -0
- package/dist/util/private-fs.js +253 -0
- package/dist/util/private-fs.js.map +1 -0
- package/dist/util/state.d.ts.map +1 -1
- package/dist/util/state.js +3 -0
- package/dist/util/state.js.map +1 -1
- package/dist/viewer/app.js +2 -2
- package/package.json +5 -5
- package/scripts/run-tests.mjs +14 -2
package/README.md
CHANGED
|
@@ -146,7 +146,7 @@ Without a map, your coding agent starts every task blind. It greps a term, opens
|
|
|
146
146
|
|
|
147
147
|
The graft map builds that understanding once, and keeps it current:
|
|
148
148
|
|
|
149
|
-
- **Every file, function and caller.** Parsed with tree-sitter into a graph of symbols and who calls what. Deterministic, no model, no network,
|
|
149
|
+
- **Every file, function and caller.** Parsed with tree-sitter into a graph of symbols and who calls what. Deterministic, no model, no network, 24 languages.
|
|
150
150
|
- **The right code with every prompt.** In Claude Code, hooks add the matching files and functions to each prompt, and show what depends on a file right after your agent edits it (the "blast radius"). Cursor, Codex and other agents get the same map through MCP tools and their instruction files.
|
|
151
151
|
- **Always fresh.** Every query checks the working tree first (a few tens of milliseconds when nothing moved) and rebuilds only what changed, so answers describe the code as it is right now, uncommitted edits included.
|
|
152
152
|
- **Plain files you can read.** Linked markdown nodes your agent opens and greps like any other file. No embeddings, no index to keep warm.
|
|
@@ -489,7 +489,7 @@ compiler-grade layer, all `$0` and deterministic (no model, no key):
|
|
|
489
489
|
- **Broad**: symbols (functions, classes, methods, types, …) plus name-resolved
|
|
490
490
|
call edges via a generic tree-sitter extractor, one grammar per language:
|
|
491
491
|
**Rust, C, C++, C#, Ruby, Scala, Elixir, Solidity,
|
|
492
|
-
Dart, Clojure, Nix, Lua**, and **OCaml, Zig** (symbols only for now, no call edges).
|
|
492
|
+
Dart, Clojure, Nix, Lua, Gleam**, and **OCaml, Zig** (symbols only for now, no call edges).
|
|
493
493
|
|
|
494
494
|
- **Compiler-grade edges (opt-in)**: `trail build --lsp` adds precise
|
|
495
495
|
`lsp_resolved` call edges (member calls the static pass can't type) when a
|
|
@@ -498,7 +498,7 @@ compiler-grade layer, all `$0` and deterministic (no model, no key):
|
|
|
498
498
|
**gopls** (Go), **pyright** (Python), **typescript-language-server** (TS/JS).
|
|
499
499
|
It's best-effort: with no server installed the graph is unchanged.
|
|
500
500
|
|
|
501
|
-
Twenty-
|
|
501
|
+
Twenty-four languages, plus Vue single-file components. A file whose language isn't listed is skipped, not
|
|
502
502
|
indexed. Adding a broad-tier language is a small contribution; see
|
|
503
503
|
[CREDITS.md](CREDITS.md) for the folks who added the current set.
|
|
504
504
|
|
|
@@ -531,7 +531,7 @@ _Summary, sources, links, and your text ship today in markdown nodes. The crux s
|
|
|
531
531
|
- **On your machine, no key, no network:** the structural code graph. `trail build` (wiring graph + per-file cards), `trail build --check`, `trail ask`, learnings and `trail check` are deterministic. They never call a model.
|
|
532
532
|
- **Through your provider key:** the LLM-written parts. `trail build --deep` adds the concept nodes (file summaries + node synthesis) and the per-symbol summaries and cruxes. The graft map is vendor-neutral: set `GRAFT_PROVIDER` (`openai` for any OpenAI-compatible endpoint, `anthropic` for the native API, or `litellm` / `orcarouter` for a gateway that speaks the OpenAI-compatible format), your `GRAFT_API_KEY`, `GRAFT_MODEL`, and, for the `openai` wire format, `GRAFT_BASE_URL` to point at OpenRouter, Fireworks, Groq, a LiteLLM proxy, a local server, or OpenAI itself. Or pass `--provider/--model/--api-key/--base-url` on the command line. (`OPENROUTER_API_KEY` still works as a deprecated fallback, and `ORCAROUTER_API_KEY` as a second one.)
|
|
533
533
|
- **Through Trail, only if you connect a workspace:** the learnings in the repos you picked, and the searches and checks that used them. See [What stays on your machine](#what-stays-on-your-machine).
|
|
534
|
-
- **Anonymous usage stats:** a daily npm version check and one batched usage ping. The ping carries buckets and fixed labels only: never your code, file paths, repo name, symbols, queries, learnings or error messages. [`TELEMETRY.md`](TELEMETRY.md) is the complete list and `trail telemetry debug` prints exactly what your machine would send.
|
|
534
|
+
- **Anonymous usage stats:** a daily npm version check and one batched usage ping. The ping carries buckets and fixed labels only: never your code, file paths, repo name, symbols, queries, learnings or error messages. [`TELEMETRY.md`](TELEMETRY.md) is the complete list and `trail telemetry debug` prints exactly what your machine would send. `DO_NOT_TRACK=1` disables both network calls; `trail telemetry disable` or unchecking the box in `trail init` disables only the usage ping. The ping is off in CI and in any build from source.
|
|
535
535
|
|
|
536
536
|
Settings keep their `GRAFT_` names. See [`.env.example`](.env.example) for the full list (model, base URL, graph directory).
|
|
537
537
|
|
package/dist/ai/crux.d.ts
CHANGED
|
@@ -41,15 +41,43 @@ export interface CruxSummarizer {
|
|
|
41
41
|
lastMiss?: CruxMiss | null;
|
|
42
42
|
}
|
|
43
43
|
/** Why a crux call produced no usable summaries (#235). */
|
|
44
|
-
export type CruxMissKind = "empty-toolCalls" | "unparseable" | "truncated" | "empty-parsed";
|
|
44
|
+
export type CruxMissKind = "empty-toolCalls" | "unparseable" | "truncated" | "empty-parsed" | "id-mismatch";
|
|
45
45
|
export interface CruxMiss {
|
|
46
46
|
kind: CruxMissKind;
|
|
47
47
|
finishReason: string | null;
|
|
48
|
+
/** `id-mismatch` only: how many entries came back, and the first one's raw id. */
|
|
49
|
+
returned?: number;
|
|
50
|
+
firstId?: string;
|
|
48
51
|
}
|
|
49
|
-
/**
|
|
50
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Classify an empty/unusable crux reply. `null` means at least one usable summary.
|
|
54
|
+
* With `requested`, a summary only counts when its id is one we asked for: enrich
|
|
55
|
+
* looks results up by node id, so a summary under any other id is never applied.
|
|
56
|
+
*/
|
|
57
|
+
export declare function classifyCruxMiss(res: ChatResponse, parsed: NodeCrux[], requested?: readonly string[]): CruxMiss | null;
|
|
51
58
|
/** Per-file error text: miss class + the provider's finish_reason (#235). */
|
|
52
59
|
export declare function formatCruxMiss(kind: CruxMissKind, finishReason: string | null): string;
|
|
60
|
+
/**
|
|
61
|
+
* The model's text for an `id-mismatch`, kept apart from {@link formatCruxMiss}:
|
|
62
|
+
* the failure gate matches words like "quota" in the error message, and a
|
|
63
|
+
* returned id can carry any source text, so it must never reach the gate.
|
|
64
|
+
*/
|
|
65
|
+
export declare function formatCruxMissDetail(miss: CruxMiss | null | undefined): string;
|
|
66
|
+
/**
|
|
67
|
+
* Map an id the model returned onto one of the ids we asked for, or null.
|
|
68
|
+
*
|
|
69
|
+
* Models echo decorated target rows instead of the bare id (#259, #298, #486).
|
|
70
|
+
* Rather than cut the string at a guessed delimiter, every rule compares against
|
|
71
|
+
* the requested ids, so the result is always an id we asked for — never a
|
|
72
|
+
* fragment of a hallucinated one — and no rule assumes which characters an id
|
|
73
|
+
* may contain. In order:
|
|
74
|
+
* 1. exact match;
|
|
75
|
+
* 2. a requested id followed by ` | ` (the row echoed from the id onwards);
|
|
76
|
+
* 3. the same two with a leading `id=` removed;
|
|
77
|
+
* 4. a row ending in ` | id=<requested id>` (the current layout echoed whole).
|
|
78
|
+
* When several requested ids qualify, the longest wins.
|
|
79
|
+
*/
|
|
80
|
+
export declare function resolveTargetId(raw: string, requested: readonly string[]): string | null;
|
|
53
81
|
/** Crux summarizer backed by any {@link ChatModel} via forced tool calling. */
|
|
54
82
|
export declare class ChatCruxSummarizer implements CruxSummarizer {
|
|
55
83
|
private model;
|
package/dist/ai/crux.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crux.d.ts","sourceRoot":"","sources":["../../src/ai/crux.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,kFAAkF;AAClF,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IACxD,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC5B;AAED,2DAA2D;AAC3D,MAAM,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"crux.d.ts","sourceRoot":"","sources":["../../src/ai/crux.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,kFAAkF;AAClF,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IACxD,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC5B;AAED,2DAA2D;AAC3D,MAAM,MAAM,YAAY,GACpB,iBAAiB,GACjB,aAAa,GACb,WAAW,GACX,cAAc,GACd,aAAa,CAAC;AAElB,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,YAAY,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAQD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,YAAY,EACjB,MAAM,EAAE,QAAQ,EAAE,EAClB,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,GAC5B,QAAQ,GAAG,IAAI,CAcjB;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAMtF;AAKD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAO9E;AAwED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAYxF;AA2CD,+EAA+E;AAC/E,qBAAa,kBAAmB,YAAW,cAAc;IAG3C,OAAO,CAAC,KAAK;IAFzB,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAQ;gBAEb,KAAK,EAAE,SAAS;IAE9B,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;CAwB9D"}
|
package/dist/ai/crux.js
CHANGED
|
@@ -5,13 +5,22 @@ function isTruncatedStop(reason) {
|
|
|
5
5
|
const r = reason.toLowerCase();
|
|
6
6
|
return r === "length" || r === "max_tokens";
|
|
7
7
|
}
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Classify an empty/unusable crux reply. `null` means at least one usable summary.
|
|
10
|
+
* With `requested`, a summary only counts when its id is one we asked for: enrich
|
|
11
|
+
* looks results up by node id, so a summary under any other id is never applied.
|
|
12
|
+
*/
|
|
13
|
+
export function classifyCruxMiss(res, parsed, requested) {
|
|
10
14
|
const finishReason = res.stopReason;
|
|
11
|
-
|
|
15
|
+
const wanted = requested ? new Set(requested) : undefined;
|
|
16
|
+
const matched = wanted ? parsed.filter((p) => wanted.has(p.id)) : parsed;
|
|
17
|
+
if (matched.some((p) => p.summary.trim()))
|
|
12
18
|
return null;
|
|
13
19
|
if (isTruncatedStop(finishReason))
|
|
14
20
|
return { kind: "truncated", finishReason };
|
|
21
|
+
if (parsed.length > 0 && matched.length === 0) {
|
|
22
|
+
return { kind: "id-mismatch", finishReason, returned: parsed.length, firstId: parsed[0].id };
|
|
23
|
+
}
|
|
15
24
|
if (parsed.length > 0)
|
|
16
25
|
return { kind: "empty-parsed", finishReason };
|
|
17
26
|
const emptyTools = res.toolCalls.length === 0;
|
|
@@ -23,8 +32,26 @@ export function classifyCruxMiss(res, parsed) {
|
|
|
23
32
|
/** Per-file error text: miss class + the provider's finish_reason (#235). */
|
|
24
33
|
export function formatCruxMiss(kind, finishReason) {
|
|
25
34
|
const fr = finishReason == null || finishReason === "" ? "null" : finishReason;
|
|
35
|
+
if (kind === "id-mismatch") {
|
|
36
|
+
return `model returned summaries but no id matched a requested target [${kind}, finish_reason=${fr}]`;
|
|
37
|
+
}
|
|
26
38
|
return `model returned no usable symbol summaries [${kind}, finish_reason=${fr}]`;
|
|
27
39
|
}
|
|
40
|
+
/** Longest echoed id shown in an error; a whole echoed row can carry a long signature. */
|
|
41
|
+
const MAX_SHOWN_ID_CHARS = 200;
|
|
42
|
+
/**
|
|
43
|
+
* The model's text for an `id-mismatch`, kept apart from {@link formatCruxMiss}:
|
|
44
|
+
* the failure gate matches words like "quota" in the error message, and a
|
|
45
|
+
* returned id can carry any source text, so it must never reach the gate.
|
|
46
|
+
*/
|
|
47
|
+
export function formatCruxMissDetail(miss) {
|
|
48
|
+
if (miss?.kind !== "id-mismatch" || miss.firstId === undefined)
|
|
49
|
+
return "";
|
|
50
|
+
const id = miss.firstId.length > MAX_SHOWN_ID_CHARS
|
|
51
|
+
? `${miss.firstId.slice(0, MAX_SHOWN_ID_CHARS)}…`
|
|
52
|
+
: miss.firstId;
|
|
53
|
+
return `first of ${miss.returned ?? 1} returned id(s): ${JSON.stringify(id)}`;
|
|
54
|
+
}
|
|
28
55
|
const SYSTEM_PROMPT = `You explain code definitions for a code graph that helps engineers navigate a codebase.
|
|
29
56
|
|
|
30
57
|
You are given ONE source file with 1-based line numbers, and a list of TARGET definitions in it. Describe EVERY target via the record_symbols tool.
|
|
@@ -63,16 +90,66 @@ function numberLines(source) {
|
|
|
63
90
|
.map((line, i) => `${i + 1}\t${line}`)
|
|
64
91
|
.join("\n");
|
|
65
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* `id=` is the LAST field of a target line, so the id ends where the line does.
|
|
95
|
+
* With the id first nothing terminated it, and models asked for the id
|
|
96
|
+
* "verbatim" copied the whole row — `a.cs#Level | variable | lines L58-L58 | sig`
|
|
97
|
+
* — into `id` (#259). {@link resolveTargetId} still repairs echoes of either layout.
|
|
98
|
+
*/
|
|
66
99
|
function userContent(input) {
|
|
67
100
|
const targets = input.nodes
|
|
68
|
-
.map((n) => `-
|
|
69
|
-
(n.signature ? ` | ${n.signature}` : "")
|
|
101
|
+
.map((n) => `- ${n.kind} | lines L${n.startLine}-L${n.endLine}` +
|
|
102
|
+
(n.signature ? ` | ${n.signature}` : "") +
|
|
103
|
+
` | id=${n.id}`)
|
|
70
104
|
.join("\n");
|
|
71
105
|
const n = input.nodes.length;
|
|
72
106
|
return `FILE: ${input.path}\n\n${numberLines(input.source)}\n\nTARGETS (${n} — return all ${n}, one entry per id):\n${targets}`;
|
|
73
107
|
}
|
|
74
|
-
/**
|
|
75
|
-
function
|
|
108
|
+
/** The longest requested id `test` accepts, or null. */
|
|
109
|
+
function longestMatch(requested, test) {
|
|
110
|
+
let best = null;
|
|
111
|
+
for (const id of requested)
|
|
112
|
+
if (test(id) && (best === null || id.length > best.length))
|
|
113
|
+
best = id;
|
|
114
|
+
return best;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Map an id the model returned onto one of the ids we asked for, or null.
|
|
118
|
+
*
|
|
119
|
+
* Models echo decorated target rows instead of the bare id (#259, #298, #486).
|
|
120
|
+
* Rather than cut the string at a guessed delimiter, every rule compares against
|
|
121
|
+
* the requested ids, so the result is always an id we asked for — never a
|
|
122
|
+
* fragment of a hallucinated one — and no rule assumes which characters an id
|
|
123
|
+
* may contain. In order:
|
|
124
|
+
* 1. exact match;
|
|
125
|
+
* 2. a requested id followed by ` | ` (the row echoed from the id onwards);
|
|
126
|
+
* 3. the same two with a leading `id=` removed;
|
|
127
|
+
* 4. a row ending in ` | id=<requested id>` (the current layout echoed whole).
|
|
128
|
+
* When several requested ids qualify, the longest wins.
|
|
129
|
+
*/
|
|
130
|
+
export function resolveTargetId(raw, requested) {
|
|
131
|
+
const id = raw.trim();
|
|
132
|
+
if (requested.includes(id))
|
|
133
|
+
return id;
|
|
134
|
+
const echoed = longestMatch(requested, (r) => id.startsWith(`${r} | `));
|
|
135
|
+
if (echoed)
|
|
136
|
+
return echoed;
|
|
137
|
+
if (id.startsWith("id=")) {
|
|
138
|
+
const bare = id.slice(3).trim();
|
|
139
|
+
if (requested.includes(bare))
|
|
140
|
+
return bare;
|
|
141
|
+
const bareEchoed = longestMatch(requested, (r) => bare.startsWith(`${r} | `));
|
|
142
|
+
if (bareEchoed)
|
|
143
|
+
return bareEchoed;
|
|
144
|
+
}
|
|
145
|
+
return longestMatch(requested, (r) => id.endsWith(` | id=${r}`));
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Normalize the tool's parsed argument object into a {@link NodeCrux} list. Ids
|
|
149
|
+
* are resolved against `requested`; one that matches nothing keeps its raw text
|
|
150
|
+
* so {@link classifyCruxMiss} can report what the model actually sent.
|
|
151
|
+
*/
|
|
152
|
+
function parseResults(obj, requested) {
|
|
76
153
|
if (!obj || !Array.isArray(obj.symbols))
|
|
77
154
|
return [];
|
|
78
155
|
const num = (v) => (typeof v === "number" && Number.isFinite(v) ? Math.trunc(v) : 0);
|
|
@@ -80,7 +157,7 @@ function parseResults(obj) {
|
|
|
80
157
|
.map((s) => s)
|
|
81
158
|
.filter((s) => typeof s.id === "string")
|
|
82
159
|
.map((s) => ({
|
|
83
|
-
id: s.id,
|
|
160
|
+
id: resolveTargetId(s.id, requested) ?? s.id,
|
|
84
161
|
summary: typeof s.summary === "string" ? s.summary.trim() : "",
|
|
85
162
|
crux_start: num(s.crux_start),
|
|
86
163
|
crux_end: num(s.crux_end),
|
|
@@ -133,8 +210,9 @@ export class ChatCruxSummarizer {
|
|
|
133
210
|
{ role: "user", content: userContent(input) },
|
|
134
211
|
],
|
|
135
212
|
});
|
|
136
|
-
const
|
|
137
|
-
|
|
213
|
+
const requested = input.nodes.map((n) => n.id);
|
|
214
|
+
const parsed = parseResults(argsFromResponse(res), requested);
|
|
215
|
+
this.lastMiss = classifyCruxMiss(res, parsed, requested);
|
|
138
216
|
return parsed;
|
|
139
217
|
}
|
|
140
218
|
}
|
package/dist/ai/crux.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crux.js","sourceRoot":"","sources":["../../src/ai/crux.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"crux.js","sourceRoot":"","sources":["../../src/ai/crux.ts"],"names":[],"mappings":"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"}
|
package/dist/ai/llm/openai.d.ts
CHANGED
|
@@ -8,6 +8,16 @@
|
|
|
8
8
|
* is forwarded, cache breakpoints become `cache_control` content parts (which
|
|
9
9
|
* OpenRouter forwards to Anthropic), and cached tokens are subtracted out of the
|
|
10
10
|
* input count so {@link Usage.input} is uncached-only.
|
|
11
|
+
*
|
|
12
|
+
* Past that, everything here is tolerance rather than preference. An
|
|
13
|
+
* OpenAI-compatible endpoint is a gateway whose own upstream calls can fail
|
|
14
|
+
* while the HTTP status stays 200, and whose support for a forced `tool_choice`
|
|
15
|
+
* — the one structured-output mechanism graft can rely on across providers —
|
|
16
|
+
* ranges from exact to none. Each way that goes wrong gets its own narrow
|
|
17
|
+
* recovery: {@link ProviderResponseError} for a 200 that is not a completion,
|
|
18
|
+
* and a bounded rung ladder in {@link OpenAIChatModel.structured} for a
|
|
19
|
+
* structured reply the endpoint answers some other way. None of it changes the
|
|
20
|
+
* bytes on the wire for an endpoint that behaves.
|
|
11
21
|
*/
|
|
12
22
|
import OpenAI from "openai";
|
|
13
23
|
import type { ChatModel, ChatRequest, ChatResponse } from "./types.js";
|
|
@@ -21,13 +31,96 @@ export interface OpenAIChatModelOptions {
|
|
|
21
31
|
headers?: Record<string, string>;
|
|
22
32
|
/** Inject a pre-built client (tests pass a stub; production omits it). */
|
|
23
33
|
client?: OpenAI;
|
|
34
|
+
/** Wait between retries of a 200-that-is-an-error (tests pass a no-op). */
|
|
35
|
+
sleep?: (ms: number) => Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A 200 that is not a completion.
|
|
39
|
+
*
|
|
40
|
+
* A proxy in front of a model can answer HTTP 200 with
|
|
41
|
+
* `{"error": {...}}` and no `choices` when the upstream call fails, because the
|
|
42
|
+
* failure belongs to a server the proxy itself did not talk to. The SDK's retry
|
|
43
|
+
* policy only ever sees the status, so it returns that body as a success, and
|
|
44
|
+
* the first `choices[0]` used to turn it into a TypeError naming nothing a user
|
|
45
|
+
* could act on. Retryable by construction — the request was never served — and
|
|
46
|
+
* carrying the provider's own message and code so the failure that does reach
|
|
47
|
+
* the caller says what the endpoint said.
|
|
48
|
+
*/
|
|
49
|
+
export declare class ProviderResponseError extends Error {
|
|
50
|
+
/** The provider's own error code, when the body carried one. */
|
|
51
|
+
readonly code?: string | number | undefined;
|
|
52
|
+
/** The status the provider actually sent. 200: that is the whole surprise. */
|
|
53
|
+
readonly status = 200;
|
|
54
|
+
/** Always true — the same request can succeed on a later attempt. */
|
|
55
|
+
readonly retryable = true;
|
|
56
|
+
constructor(message: string,
|
|
57
|
+
/** The provider's own error code, when the body carried one. */
|
|
58
|
+
code?: string | number | undefined);
|
|
24
59
|
}
|
|
25
60
|
export declare class OpenAIChatModel implements ChatModel {
|
|
26
61
|
readonly label: string;
|
|
27
62
|
private client;
|
|
28
63
|
private model;
|
|
64
|
+
private sleep;
|
|
65
|
+
/** Key into {@link forcedToolChoiceIgnored}: one entry per endpoint + model. */
|
|
66
|
+
private choiceKey;
|
|
67
|
+
private forcedIgnored;
|
|
29
68
|
constructor(opts: OpenAIChatModelOptions);
|
|
30
69
|
create(req: ChatRequest): Promise<ChatResponse>;
|
|
70
|
+
/**
|
|
71
|
+
* A structured reply, however the endpoint is willing to produce one.
|
|
72
|
+
*
|
|
73
|
+
* A forced `tool_choice` is the only structured-output mechanism graft can
|
|
74
|
+
* rely on across providers, and it is the least portable corner of the
|
|
75
|
+
* OpenAI-compatible spec: an endpoint can reject the object form (handled one
|
|
76
|
+
* layer down), answer 200 with an error object, or quietly answer in prose.
|
|
77
|
+
* So walk a bounded ladder — the object form, then `"required"`, then
|
|
78
|
+
* `"auto"` with the tool named in the prompt — and take the payload from
|
|
79
|
+
* `content` if that is where it landed. `"required"` only stands in for the
|
|
80
|
+
* object form when one tool was offered, the same rule the 400 fallback
|
|
81
|
+
* applies: with several tools it would let the model pick freely rather than
|
|
82
|
+
* honour the caller's choice.
|
|
83
|
+
*
|
|
84
|
+
* An endpoint that honours the forced form never sees rung two — the first
|
|
85
|
+
* attempt answers the question and the ladder ends.
|
|
86
|
+
*
|
|
87
|
+
* An errored body is a weaker signal than a served-but-wrong reply, since it
|
|
88
|
+
* may belong to an upstream that a retry would reach, so it never downgrades
|
|
89
|
+
* on its own: it is counted instead. A call where every forced-family rung
|
|
90
|
+
* failed that way and the final "auto" rung then answered is a rescue, and
|
|
91
|
+
* the second rescue with no successful forced call in between is the
|
|
92
|
+
* endpoint's pattern, not its upstream's — that, and only that, switches
|
|
93
|
+
* later calls straight to "auto".
|
|
94
|
+
*/
|
|
95
|
+
private structured;
|
|
96
|
+
/** One rung: the forced object form, its equivalent string form, or "auto" plus a prompt. */
|
|
97
|
+
private attempt;
|
|
98
|
+
/** Did the reply carry the payload the caller forced a tool for? */
|
|
99
|
+
private delivered;
|
|
100
|
+
/**
|
|
101
|
+
* One more rescued call for this endpoint + model. When the count reaches
|
|
102
|
+
* the threshold with no successful forced call in between, the endpoint is
|
|
103
|
+
* marked the same way a served-but-wrong reply marks it.
|
|
104
|
+
*/
|
|
105
|
+
private noteForcedRescue;
|
|
106
|
+
/**
|
|
107
|
+
* Remember, for this process, that the endpoint did not answer a forced
|
|
108
|
+
* `tool_choice` with a tool call, so later requests go straight to the rung
|
|
109
|
+
* that works. Logged once per endpoint + model — the set is shared by every
|
|
110
|
+
* instance, so a second adapter for the same endpoint never repeats it: it
|
|
111
|
+
* changes the bytes on the wire, which is exactly what someone reading a
|
|
112
|
+
* failing build needs told.
|
|
113
|
+
*/
|
|
114
|
+
private rememberForcedIgnored;
|
|
115
|
+
/**
|
|
116
|
+
* One completion, with the transport's retry policy extended to a 200 that
|
|
117
|
+
* carried an error instead of one. The SDK retries 429, 5xx and connection
|
|
118
|
+
* failures but never a successful status, and that body is the endpoint
|
|
119
|
+
* reporting a failed upstream call — the same judgement, so the same
|
|
120
|
+
* `GRAFT_LLM_RETRIES` budget and backoff apply, and the typed error is what
|
|
121
|
+
* reaches the caller once that budget is spent.
|
|
122
|
+
*/
|
|
123
|
+
private complete;
|
|
31
124
|
/**
|
|
32
125
|
* Wraps `chat.completions.create` with a narrow, safe fallback: if the
|
|
33
126
|
* server rejects an object-form `tool_choice` and exactly one tool was
|
|
@@ -39,6 +132,14 @@ export declare class OpenAIChatModel implements ChatModel {
|
|
|
39
132
|
* automatically.
|
|
40
133
|
*/
|
|
41
134
|
private createChatCompletion;
|
|
135
|
+
/**
|
|
136
|
+
* Normalize provider message content to the plain text representation
|
|
137
|
+
* expected by the rest of Graft. Some OpenAI-compatible providers
|
|
138
|
+
* (observed with GLM-5.3 hosted on Mistral's platform) return content as
|
|
139
|
+
* structured content parts, e.g. `[{ type: "text", text: "..." }]`,
|
|
140
|
+
* instead of a plain string — this path in Graft assumed the latter.
|
|
141
|
+
*/
|
|
142
|
+
private static contentToText;
|
|
42
143
|
private fromResponse;
|
|
43
144
|
}
|
|
44
145
|
//# sourceMappingURL=openai.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/ai/llm/openai.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/ai/llm/openai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAsC,MAAM,YAAY,CAAC;AAsD3G,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAuGD;;;;;;;;;;;GAWG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAO5C,gEAAgE;IAChE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;IAPjC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,OAAO;IACtB,qEAAqE;IACrE,QAAQ,CAAC,SAAS,QAAQ;gBAExB,OAAO,EAAE,MAAM;IACf,gEAAgE;IACvD,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,YAAA;CAKlC;AAiGD,qBAAa,eAAgB,YAAW,SAAS;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,KAAK,CAAgC;IAC7C,gFAAgF;IAChF,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAAU;gBAEnB,IAAI,EAAE,sBAAsB;IAgBlC,MAAM,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IA6BrD;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;YACW,UAAU;IA2CxB,6FAA6F;IAC7F,OAAO,CAAC,OAAO;IAMf,oEAAoE;IACpE,OAAO,CAAC,SAAS;IAKjB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAOxB;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAS7B;;;;;;;OAOG;YACW,QAAQ;IAgBtB;;;;;;;;;OASG;YACW,oBAAoB;IAkClC;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;IAe5B,OAAO,CAAC,YAAY;CAgDrB"}
|