Skip to content

feat(agent): multi-agent apps with per-agent skills, tools, and optional delegation - #2354

Closed
ariskemper wants to merge 6 commits into
mainfrom
feat/multi-agent-skills
Closed

ariskemper wants to merge 6 commits into
mainfrom
feat/multi-agent-skills

Conversation

@ariskemper

@ariskemper ariskemper commented Jun 11, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Lets a Veryfront app run one or many specialized agents, each with its own settings, its own SKILL.md, and its own tools, with opt-in orchestration. Previously markdown agents could only set persona/model/steps and could not bind project tools at all; skills were global and shared.

Motivation

Teams want to ship a crew of focused agents (a researcher, a writer, a reviewer) that each carry their own knowledge (skills) and actions (tools), and to optionally place a coordinator in front of them — without hand-wiring getAgentsAsTools in code or leaking one agent's capabilities into another. This makes that a first-class, file-based convention.

What changed

Colocated directory layout — an agent owns its capability surface:

agents/
  lead.md                         # coordinator: delegates: [researcher, writer]
  researcher/
    AGENT.md                      # settings + instructions (frontmatter)
    SKILL.md                      # own skill        -> load_skill("researcher")
    skills/cite/SKILL.md          # nested skill     -> load_skill("researcher__cite")
    tools/fetch-paper.ts          # own tool         -> "researcher__fetch-paper"
  writer/
    AGENT.md
    SKILL.md
workflows/                        # app-level orchestration stays global
skills/  tools/                   # optional shared/global capabilities
  • Frontmatter selectors: skills: true | [..], tools: true | [..], delegates: [..].
  • Orchestration is optional: with delegates:, an agent gets agent_{id} tools that run named specialists (each with their own settings/skills/tools); without it, agents are independent and selected by agentId. Flat agents/{id}.md is unchanged (back-compat).
  • Namespacing & isolation: colocated capabilities register as {sanitizedAgentId}__{name} (provider-tool-name safe). A colocated agent's skills: true resolves to its own explicit id list, never the registry-wide true — agents never see each other's skills.
  • Why workflows stay global: a workflow orchestrates agents (sits above them); the agent-level orchestration primitive is delegates:. prompts/, resources/, tasks/ likewise stay global.

Design notes: docs/proposals/multi-agent-skills.md. User docs: docs/guides/agents.md (per-agent skills & tools + frontmatter table) and docs/guides/multi-agent.md (declarative delegation).

Acceptance criteria

  • An app can define multiple agents, discovered from agents/ (directory and flat layouts side by side).
  • Each agent has its own settings (model, temperature, max-steps, thinking, provider-tools).
  • Each agent can own a SKILL.md plus additional skills/**, scoped and namespaced to that agent; loadable via load_skill.
  • Each agent can own tools/*.ts, namespaced and bound to that agent's tool set.
  • skills: / tools: frontmatter select all (true) or a subset (list) of colocated capabilities.
  • Orchestration is opt-in: delegates: turns an agent into a coordinator with agent_{id} delegate tools; absent ⇒ independent agents. Self-delegation is rejected.
  • No capability leakage: one agent's skills: true never surfaces another agent's skills (regression-tested).
  • Back-compat: existing flat agents/{id}.md + global skills//tools/ behave exactly as before.
  • Robust discovery: duplicate agent ids, sanitize-collisions, invalid SKILL.md, and unsafe tool names are reported as discovery errors without aborting other agents.
  • Security: ./.. path segments rejected (defense-in-depth traversal).
  • Documented in guides; tested (unit + regression); reviewed via /deep-review.
  • CI green.

Testing

  • Feature + regression unit tests (parsing, discovery, colocated tool loading, skill registration, namespacing, delegation success/failure, leak guard, traversal guard, fsAdapter branches, back-compat).
  • /deep-review (simplify + correctness + security): fixes for tool-name validation, namespace-collision & duplicate-id reporting, tool merge precedence, a skill-leak path, and path-traversal hardening — each with a regression test.
  • New public exports reflected in src/agent/index.ts.

Review notes

  • TDD: not used — tests were written after implementation, then hardened by /deep-review (stated transparently).
  • api-reference: auto-generated via deno task docs; left untouched to avoid committing generator/formatter drift (my local generator output differs in formatting from the committed, canonically-formatted pages). The feature is fully documented in the guides.
  • Follow-up: the hosted (cloud) runtime resolves skills via a separate RuntimeSkillDefinition catalog; feeding colocated skills into that path is deferred (the local/factory path is fully wired) to avoid shipping an unwired loader.

Type of Change

  • New feature (non-breaking change that adds functionality)
  • Documentation update
  • Test update

Checklist

  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works

… apps

Support apps with one or many specialized markdown agents, each with its own
settings and its own SKILL.md, with orchestration being opt-in.

- agent-definition: parse new `skills` (true | string[]) and `delegates`
  (string[]) frontmatter fields.
- discovery: recognize the colocated directory layout `agents/{id}/AGENT.md`
  (+ `SKILL.md` / `skills/`) alongside the flat `agents/{id}.md` form;
  nested SKILL.md and references are ignored as agents.
- agent-scoped-skill-catalog: load an agent's own colocated skills
  (`{root}/SKILL.md` + `{root}/skills/**`), filtered by the `skills` selector.
- agent-delegation: opt-in `delegates` build `agent_{id}` tools that run named
  specialist agents (lazy resolution, self/dupe excluded). No delegates =>
  no orchestration.
- markdown adapter: thread skills/delegates/rootPath through to the runtime
  agent and expose markdown metadata.

Adds focused unit tests for parsing, discovery, scoped-catalog loading, and
delegation. Plan in docs/proposals/multi-agent-skills.md.
Extend the colocated multi-agent model so each agent can own its tools the
same way it owns skills, and wire both into the running agent.

- agent-definition: new `tools` selector (true | string[]), parsed via a
  shared capability-selector helper alongside `skills`.
- agent-scoped-capabilities (new): load `agents/{id}/tools/*.ts` as a
  namespaced Tool record ({sanitizedAgentId}__{name}, provider-safe) and
  register `agents/{id}/SKILL.md` + `skills/**` as Skill objects in the skill
  registry (own skill = agent id, nested = {id}__{sub}).
- discovery handler: resolve colocated capabilities for directory agents and
  pass resolved skill ids + tool record to the adapter.
- markdown adapter: resolved skill ids -> config.skills (explicit list, never
  registry-wide `true` so agents don't leak each other's skills); colocated +
  delegate tools merged into config.tools.

Workflows/prompts/resources/tasks stay global by design — workflows orchestrate
agents (above them); agent-level orchestration is `delegates`.

Adds unit tests for the tools loader, skill registration, namespacing, and an
end-to-end discovery assertion that colocated skills/tools land on the agent
config. Updates docs/proposals/multi-agent-skills.md.
Pass 1 — simplify:
- Remove orphaned agent-scoped-skill-catalog.ts (+ test, + exports): exported
  but never consumed; the factory path uses agent-scoped-capabilities.ts.
- Flatten createRuntimeAgentFromMarkdownDefinition from (Definition | Input) to
  (definition, options?), dropping the 'definition in input' discriminator.
- Resolve colocated skills + tools concurrently (disjoint subtrees).

Pass 2 — correctness:
- Skip + report colocated tools whose namespaced name violates the provider
  tool-name charset / 64-char limit.
- Detect and report the sanitize-collision case (e.g. 'a.b' vs 'a_b' -> 'a_b')
  instead of silently overwriting one agent's capabilities.
- Report duplicate agent ids (flat 'x.md' + dir 'x/') instead of silent first-wins.
- Colocated tools take precedence over delegate tools on key collision.
- Fix skill-leak: directory agents now always resolve to their own explicit
  skill-id list (even empty) and never fall back to the registry-wide 'true',
  which would surface other agents' skills. Caught by a new regression test.
- Add tests: delegate success path, factory namespaced-tool normalization,
  resolvedSkillIds override/empty-guard, flat back-compat, invalid SKILL.md ->
  recorded error (agent still registers), duplicate id, namespace collision.

Pass 3 — security:
- Reject '.' / '..' path segments in agent-dir, flat-id, and nested-skill-dir
  names (defense-in-depth path traversal) via isSafePathSegment; add a
  security regression test.

Tests: 50 groups / 202 steps pass. Typecheck/lint clean (pre-existing react
esm.sh typing error unrelated).
CI fix (the actual failure):
- The 4 unit tests that load colocated tools via importModule failed in CI with
  'op_read started but never completed' — importModule transpiles via esbuild,
  which keeps a child process alive across tests. Disable op/resource sanitizers
  on those tests, matching src/discovery/transpiler.test.ts. (Coverage gate also
  failed only because it shares the unit suite under --fail-fast.)

Coverage:
- Add src/discovery/file-discovery.test.ts covering the new fsAdapter branches
  of listDiscoveryDirectoryEntries / discoveryFileExists.
- Cover the markdown metadata accessors and resolvedSkillIds override/empty-guard
  in agent-markdown-adapter.test.ts (function coverage 57% -> 100%).

Docs:
- guides/agents.md: per-agent skills & tools (directory layout), colocated
  namespacing, and the markdown-agent frontmatter table (skills/tools/delegates).
- guides/multi-agent.md: declarative delegation with the delegates frontmatter.
- api-reference is generated (deno task docs); left untouched here to avoid
  committing generator/formatter drift.
@ariskemper
ariskemper force-pushed the feat/multi-agent-skills branch from b384420 to eb8fc65 Compare June 11, 2026 08:25
The colocated-tool discovery tests transpile tool modules via importModule
(esbuild), which keeps a warm child process alive across tests and trips Deno's
op/resource leak sanitizers — the same reason src/discovery/transpiler.test.ts
opts out. Raise SANITIZER_OPT_OUT_BASELINE 420 -> 428 to track these 4 tests
(2 opt-outs each).
@kojiwakayama

Copy link
Copy Markdown
Contributor

Review: this PR mixes several concerns, and one of them hides a real leak

Overall the per-file code quality is solid — lazy delegate resolution so discovery order doesn't matter, errors reported without aborting sibling agents, sanitize-collision detection, ./.. hardening, deterministic sorting, good tests and docs. The concerns below are about the design surface and PR scope, not sloppy code.

1. PR scope: this is three features, one fully orthogonal

  1. Directory layout — agents/{id}/AGENT.md as an alternative to agents/{id}.md (colocation convention)
  2. Per-agent capability discovery & scoping — colocated tools/*.ts and SKILL.md/skills/**, namespacing, skills:/tools: selectors, leak isolation
  3. Delegation — delegates: frontmatter → agent_{id} tools

(3) is the easiest to argue out: agent-delegation.ts has zero dependency on colocation — it works identically for flat agents and resolves delegates lazily from the global registry. It's a standalone feature with its own docs section and could merge independently. The new file-discovery.ts helpers (listDiscoveryDirectoryEntries, discoveryFileExists) are likewise a standalone infra refactor.

2. Design: capability binding is coupled to file layout

This coupling produces user-visible semantic inconsistencies:

  • skills: true means two different things depending on layout. For a flat agents/x.md agent it means "every skill in the global registry" (old behavior, preserved via resolveSkillsConfig falling through to definition.skills). For a directory agent it means "only my own colocated skills". Same frontmatter key, same docs table row, two semantics — the meaning of a binding declaration depends on where the file sits.
  • tools: is a silent no-op for flat agents. It's accepted by the schema in agent-definition.ts, parsed, then dropped — resolveColocatedCapabilities returns {} when there's no rootPath, and nothing warns. A user writing tools: [my-tool] in a flat agent gets nothing, silently.
  • Markdown agents still can't bind global tools by name. tools: only selects colocated ones, so selector semantics are layout-dependent rather than "ids resolve against the registry, wherever they were discovered from".
  • The no-leak invariant has no single home. It's enforced cooperatively across three files: the handler always passes resolvedSkillIds (even empty) for directory agents, the adapter's resolveSkillsConfig knows not to fall back to true when the array is empty-but-present, and the capabilities module does the namespacing. Each piece carries a comment explaining the others' behavior — a sign one concern is smeared across layers.

3. Probable bug: the leak isn't actually closed (blocking)

registerAgentColocatedSkills registers colocated skills into the global skill registry (agent-scoped-capabilities.ts). But skillRegistry.resolveForAgent(true) is getAll() (src/skill/registry.ts), resolved at invocation time (src/agent/factory.ts). So:

  • a flat markdown agent with skills: true, or any TS agent with skills: true, sees every directory agent's colocated skills (researcher, researcher__cite, …).

Isolation is one-directional: directory agents can't see each other, but everyone else with skills: true sees all of them. The acceptance criterion "no capability leakage … (regression-tested)" appears to only cover the directory→directory direction. This falls directly out of the mixed design: scope straddles two mechanisms — a shared global registry (discovery concern) and per-agent id lists (binding concern) — instead of scope being a property of the registration itself.

Smaller note in the same vein: in agent-scoped-capabilities.ts the tool path is pure (returns a record) while the skill path side-effects the global registry — asymmetric for two functions that are conceptually the same operation, and the module mixes layout-walking, module loading, namespacing policy, selector filtering, and registry mutation.

Suggested path

  1. Split out delegates: into its own PR — independent and mergeable as-is.
  2. Split out the discovery infra helpers (trivial, uncontroversial).
  3. For the core feature, decouple binding from layout: colocation becomes purely a discovery + namespacing convention that feeds the registry with scope-tagged entries; skills:/tools: become a uniform binding declaration resolving ids against the registry regardless of layout. That fixes the skills: true double meaning, the silent tools: no-op for flat agents, and — if registry entries carry their owning scope — the global-true leak, in one move.

At minimum, item 3 (the leak) should be tested/fixed before merge.

@kojiwakayama

Copy link
Copy Markdown
Contributor

Follow-up: a concrete path to adopt these ideas within existing conventions

Following up on my review above — the ideas here (colocation, per-agent capabilities, declarative delegation) are good and fit the architecture. Only the scoping mechanism conflicts with existing conventions. Here's a redesign path that keeps the feature, closes the leak, and ends up smaller.

What the current conventions already give us

  1. Discovery feeds flat, project-scoped registries; binding is by id. skills/ → skill registry (id = directory name, skill-handler.ts), tools/ → tool registry (tool-handler.ts). Agents bind by id list, resolved at invocation time (factory.ts — which is also what keeps HMR working).
  2. Registration ≠ visibility — even today. The factory already registers every TS agent's "own" tools into the global tool registry (factory.ts, the config.tools loop); what scopes them to the agent is the binding, not where they're registered. The colocated-tools handling in this PR is actually consistent with that.
  3. The registry infra already has a scope-tier concept. ScopedRegistryFacade distinguishes shared (framework) vs project entries (registerShared / getOwn). There's precedent for "entries carry a scope" — just not yet a per-agent tier.

The only place this PR genuinely fights a convention is skills: true: today it means "everything in the registry" (resolveForAgent, src/skill/registry.ts) — visibility by registry membership. The PR keeps that for flat agents but builds a parallel mechanism (handler-resolved resolvedSkillIds + empty-array sentinel in the adapter) for directory agents. That parallel mechanism is the source of both the skills: true double semantics and the one-directional leak.

Proposed change: ownership lives in the registry, not in the handler/adapter

Instead of the handler/adapter id-list plumbing, registration records ownership — either Skill.owner?: string or a third registry tier alongside shared/project (which ScopedRegistryFacade is already shaped for). Then resolveForAgent(config, agentId) applies one rule for every agent kind (flat md, directory md, TS):

  • skills: true → all project-level (unowned) skills plus my own — never another agent's
  • skills: [...] → ids, resolved own-scope short names first, then global ids
  • same resolution for tools: in frontmatter, against the tool registry — which fixes the silent no-op for flat agents and gives markdown agents tool-binding parity with TS agents

This single rule fixes all three review findings at once: the skills: true double meaning disappears, the global-true leak closes in both directions, and the no-leak invariant lives in exactly one place (the registry) instead of being enforced cooperatively across the handler, the adapter, and the capabilities module.

What stays from this PR unchanged:

  • the directory layout (agents/{id}/AGENT.md + colocated SKILL.md/skills/**/tools/*.ts) — it becomes a pure additional discovery source
  • the {agentId}__{name} namespacing — it becomes the id rule for agent-owned entries in a flat registry (globally unique, provider-safe), a natural extension of "skill id = directory name"
  • delegates: and agent-delegation.ts exactly as written
  • the discovery hardening (sanitize-collision detection, ./.. guards, error reporting)

Why this is cheap right now

  • resolveForAgent(true) change is backward compatible: existing projects have no agent-owned skills, so "all unowned + own" returns exactly what getAll() returns today.
  • No back-compat burden — nothing is live yet; this is the cheapest moment to avoid shipping two binding semantics.
  • It unblocks the deferred cloud path: the hosted runtime resolves skills via a separate RuntimeSkillDefinition catalog. An owner tag serializes naturally into that catalog (id + owner), whereas the handler-side id-list mechanism doesn't exist in the cloud path at all — which is exactly why it had to be deferred in this PR.

Suggested merge order

  1. delegates: + the file-discovery.ts helpers — no conflicts, mergeable now as their own PR(s).
  2. Owner tag / agent tier in the registries + the one-rule resolveForAgent(config, agentId).
  3. Directory-agent discovery registering colocated skills/tools into the existing registries with namespaced ids and owner tags; frontmatter skills:/tools: resolve uniformly for flat and directory agents alike.

Happy to discuss alternatives — but the key ask is that scope becomes a property of registration, so binding (skills:/tools:) can stay a uniform id-based declaration regardless of file layout.

@kojiwakayama

Copy link
Copy Markdown
Contributor

Cross-platform check: three integration conflicts to settle before merge

I checked how the rest of the platform (control plane and Studio) interacts with the conventions this PR introduces. Existing projects are safe — flat TS/md agents behave identically everywhere. But three of the PR's choices conflict with established platform-wide conventions:

1. __ namespacing collides with the integration tool naming convention (blocking)

{agentId}__{name} uses the same double-underscore separator the platform already reserves for MCP integration tools (<provider>__<tool>, e.g. gmail__search_emails). Tool names of that shape are parsed and classified by the separator on both the control plane and in the Studio chat UI — and the left half is not always validated against the connector catalog. A colocated tool like researcher__fetch-paper is shape-indistinguishable from an integration tool: it will render as an integration of provider "researcher" in chat, and risks misclassification anywhere tool names are routed or authorized by shape.

The agent_{id} delegate tools are fine (no __). But the colocated-capability separator needs to change — options: a different separator within the provider-safe charset, a reserved prefix (e.g. registering under the internal veryfront namespace), or an explicit handshake with the integration naming scheme. This is a user-visible naming decision that's painful to change after launch, so it should be settled cross-team first.

2. Colocated skills won't load in the hosted runtime (confirms the deferral — but it shapes the design)

The hosted runtime resolves skills through the platform skill catalog, which assumes skills live at skills/{skillId}/SKILL.md. A namespaced colocated skill id like researcher__cite resolves to a non-existent path, so hosted load_skill will fail for every colocated skill. The PR's review notes already defer this, but it means the feature ships local-runtime-only. This is another argument for the registry/catalog-ownership approach from my earlier comment: a catalog entry that carries id + owner + actual path serializes naturally into the hosted skill catalog, whereas the handler-side id-list mechanism has no hosted equivalent at all.

3. Authoring surfaces will silently fight the new frontmatter and layout

  • Platform editing UIs validate agent frontmatter against a known field set; round-tripping a definition through them can silently strip skills:/tools:/delegates: on save — data loss for anyone editing a directory agent in the UI.
  • The existing platform frontmatter vocabulary already includes an allowed-tools field; introducing tools: creates two competing tool-selection vocabularies for agent frontmatter. These should be unified (or one deprecated) before this ships.
  • Agent scaffolding across the platform creates flat agent files and isn't aware of directory agents — creating an agent whose id matches an existing agents/{id}/ directory produces the duplicate-id discovery error this PR adds. Fine as an error, but the creation flows need to check for directory agents to avoid guiding users into the collision.
  • Editor/open-agent flows assume a flat file per agent id, so directory agents won't be editable through them until updated.

Suggested sequencing

None of this blocks the ideas — but items 1 (separator) and the tools: vs allowed-tools vocabulary in item 3 are platform-wide naming contracts that must be agreed before the framework merge, because they leak into user-visible tool/skill ids. Item 2 is best solved by the ownership-catalog redesign rather than patched afterward. The remaining authoring-surface updates can land as fast-follows once the conventions are fixed.

@kojiwakayama

Copy link
Copy Markdown
Contributor

I would not merge this as-is, although the direction is worth pursuing. The concern is not the directory-agent idea itself; it is that the current implementation makes capability ownership look scoped while still relying on global registries underneath.

The main blocker is colocated skills being registered globally while skills: true still means "all registered skills" in existing agent surfaces. That means a capability that reads as agent-owned can still become visible outside that owner. There is a second enforcement issue in the shared skill tools: loading a skill by id needs to be checked against the calling agent allowed-skill set, not just resolved from the global registry. Without that, isolation is prompt/config-level rather than runtime-enforced.

I would split this before adoption:

  1. Land delegation separately, with provider-safe delegate tool ids and explicit validation for invalid or self delegation.
  2. Land reusable discovery helpers separately if they stand on their own.
  3. Add ownership metadata to the skill/tool registries before merging colocated capabilities.
  4. Make load_skill, load_skill_reference, and execute_skill_script enforce the caller agent scope.
  5. Define one binding rule across TS agents, flat markdown agents, and directory markdown agents: true should mean global unowned capabilities plus the agent own capabilities, not every agent owned capabilities; explicit short names should resolve own first, then global ids.
  6. Only document directory-agent capabilities as stable once the rest of the product surface has the same ownership and reference model.

There are also smaller correctness issues worth fixing in this PR or the split follow-ups: tools: is accepted for flat markdown agents but appears to no-op, delegate tool ids are not sanitized the same way colocated tool ids are, and the unrelated lockfile/sanitizer-baseline churn should be separated or justified.

So my recommendation is: keep the idea, do not keep this shape. The stable version needs owner-scoped registries and enforcement at the tool boundary before it becomes a convention.

@kojiwakayama

Copy link
Copy Markdown
Contributor

Verified the three new claims from the comment above against the branch — all three hold, with file references, plus one addition:

1. load_skill scope enforcement gap — confirmed, and slightly worse than stated. createLoadSkillTool resolves skillRegistry.get(input.skillId) with no check against the calling agent's allowed-skill set (src/skill/tools.ts). Any agent with skill tools enabled can load any other agent's colocated skill by id, regardless of the manifest scoping in this PR. Addition: on a lookup miss, the error message returns skillRegistry.getAllIds().join(", ") — it enumerates every registered skill id to the caller, so an agent doesn't even need to guess ids to discover other agents' skills. Isolation is currently prompt/manifest-level only; the tool boundary (load_skill, load_skill_reference, execute_skill_script) must enforce caller scope, and the not-found error should only list skills visible to the caller.

2. Delegate tool ids unsanitized — confirmed. buildAgentDelegateTools emits agent_${id} with the raw delegate id (src/agent/runtime/agent-delegation.ts). Flat agent filenames allow dots (MARKDOWN_AGENT_FILE_PATTERN accepts [A-Za-z0-9._-]+), so delegates: [data.fetcher] produces tool id agent_data.fetcher — violating the provider charset [A-Za-z0-9_-] this PR itself enforces for colocated tools via PROVIDER_TOOL_NAME_REGEX. Delegate ids should get the same sanitize-or-reject treatment as colocated tool names.

3. Lockfile/sanitizer churn — confirmed (new commit 562a1d88a). The sanitizer baseline bump (420→428) is justified by the commit message — the new colocated-tool tests transpile via esbuild and trip Deno's leak sanitizers, same as the existing transpiler.test.ts opt-out. The deno.lock rewrite (~90 deleted lines) looks like incidental regeneration unrelated to the feature and should be split out or justified.

These findings are consistent with the rest of the thread, and the 6-step split above composes cleanly with the registry-ownership path proposed earlier: ownership metadata in the registries (step 3) makes the binding rule (step 5) expressible, and tool-boundary enforcement (step 4) is what makes it actually hold at runtime — point 1 shows config-level scoping alone is bypassable today.

kojiwakayama added a commit that referenced this pull request Jun 11, 2026
Extracted from the multi-agent staging branch (PR #2354) as standalone
infrastructure: listDiscoveryDirectoryEntries lists immediate entries of a
discovery directory and discoveryFileExists checks path existence, both
fsAdapter-aware with Node fallback. No behavior changes to existing discovery.
@kwakayama
kwakayama deleted the feat/multi-agent-skills branch July 3, 2026 21:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants