mirror of
https://gh-proxy.org/https://github.com/klinsmaya/multica.git
synced 2026-11-08 17:36:45 +08:00
admin-workspace-create2
11
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8d7e6f6a0a |
perf(server): resolve only the requested skill bundles (#7810)
The daemon resolves one skill bundle per request -- deliberately, so each download gets its own size-scaled deadline and is cached independently (GH #4505). ResolveTaskSkillBundles answered each of those by calling LoadAgentSkillBundles, which reads and hashes EVERY skill the agent has, then picks the single requested bundle out of the result and discards the rest. So the per-request cost scaled with the agent's skill count, and the number of requests did too. A cold dispatch for an agent with N skills read the whole skill set N+1 times and computed N*(N+1) bundle hashes to deliver N bundles. #7689 cut each of those loads from N+1 queries to 2, which fixed the round-trip count but not the multiplier: the bytes read from Postgres and the SHA-256 work stayed quadratic, and because the daemon issues these requests serially, that work lands directly in prepare latency. Load only what was asked for: - ListAgentSkillsByIDs is ListAgentSkills narrowed to a set of ids. The junction predicate is unchanged, so it is still the authorization: an id the agent does not have enabled returns no row, which the caller reports as not-found. It is deliberately NOT workspace-scoped, because ListAgentSkills is not either -- a stricter resolve could 404 a ref that claim had just advertised, breaking prepare instead of protecting it. - Files come from ListSkillFilesBySkillIDs, the batch query #7689 added, for those skills only. - Builtins resolve by name from the embedded FS and touch no query. - Only the requested skills are hashed. For 15 skills / 600KB that takes a cold dispatch from ~9.6MB read and 240 hashes to ~1.2MB and 30, with no schema change, no new index, and no change to the wire contract: stale-hash still returns the current bundle, plugin hash pinning still conflicts, response order still follows the request, and a read failure still fails closed with a 500 rather than a 404 the daemon would treat as permanent. LoadAgentSkills and the new loader share one assembly helper, so claim and resolve cannot drift on skill order, per-skill file order, or the nil file list of a skill with no files. Tests: an agent with three skills resolving one must issue one scoped query for one id and never the full ListAgentSkills; a builtin ref must touch no query at all; request order and ref dedup; and the denials the old full-set lookup used to provide -- unassigned skill, disabled assignment, another workspace's skill, unparseable id, a source with no server-side producer -- plus a scoped-read failure returning 500. Both the scoped load and the enabled predicate are mutation-verified. Co-authored-by: J <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
be93feb063 |
MUL-6792: perf(server): batch skill-file loads in LoadAgentSkills (#7689)
* perf(server): batch skill-file loads in LoadAgentSkills LoadAgentSkills issued one ListSkillFiles query per skill inside a loop, so an agent with N skills cost N+1 round trips on the task-claim hot path (three callers in daemon.go). Each query also pulls full file bodies, so the fixed per-query overhead (pool acquire, RTT, parse) was paid N times. Add ListSkillFilesBySkillIDs (skill_id = ANY(...), ordered by skill_id, path — backed by idx_skill_file_skill) and load all files in one query, grouping by skill_id in a single linear pass. N+1 becomes 2. Output order is preserved by assembling in ListAgentSkills order. Total bytes transferred are unchanged; this removes round trips and pool acquires, not payload. The per-skill ListSkillFiles is retained for the single- skill handler callers that legitimately need it. Co-Authored-By: Claude Fable 5 <[email protected]> * fix(server): fail closed when the agent skill read fails Batching the per-skill ListSkillFiles loop into one query changed what a swallowed read error means. Before, one failed query dropped one skill's supporting files. Now a single failure drops the files of EVERY skill -- and the `files, _ :=` discard let claim and resolve carry on as if that were the agent's real skill set. Nothing downstream can catch it. BuildAgentSkillBundles hashes whatever loaded, so the refs sent at claim and the bundle served at resolve agree with each other; the daemon's validateSkillBundle compares a bundle against a ref derived from that same bundle, accepts it, and writes it to the on-disk cache. The agent then starts on skills whose rules are missing, and the next dispatch reuses the cached truncated copy. The same was true of the ListAgentSkills error above it, which could silently strip every workspace skill from an agent. LoadAgentSkills and LoadAgentSkillBundles now return the error, and the three call sites settle it: - claim (both the slim skill-refs path and the legacy inline-skills path) refuses with 500 via rejectClaimSkillLoad and leaves the task dispatched, so the stale-dispatched reclaim redelivers it -- the same treatment rejectClaimSourceLoad gives a transient source read. - ResolveTaskSkillBundles returns 500, which the daemon's existing resolve retry schedule already treats as transient, instead of serving a bundle built from a failed read. Tests: service-level coverage for both read failures, for LoadAgentSkillBundles failing closed rather than returning built-ins only, and for the batch grouping itself -- the fixture puts the two result sets in deliberately different orders (skills by name, files by skill_id) so a positional zip would hand each skill another skill's files, and asserts the per-skill query is no longer issued. Handler-level coverage asserts the 500s and that a refused claim leaves the task dispatched and unstarted. Co-Authored-By: J <[email protected]> Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: Claude Fable 5 <[email protected]> Co-authored-by: J <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
54027ba763 |
MUL-6639: fix(skills): metadata-only skill listings and stall-based timeouts (#7574)
* MUL-6639: feat(skills): list skill files as metadata, not content `multica skill get` and `multica skill files list` both downloaded every file body to render output that shows none of them — `skill get`'s table view prints four columns, and the file listing prints paths and timestamps. Past a few hundred KB that stopped being waste and became a hard failure: a 599KB skill could not be fetched at all, and the listing that would have shown which file was oversized required downloading all of them first, so the diagnostic died with the thing it was meant to diagnose (GH #7498). Both endpoints now take `?include=content|metadata`. The metadata shape carries path, byte size and content hash per file, plus the size and hash of the SKILL.md body — computed in Postgres, so the bodies never leave the database. Response size now tracks file count, not content size. `GET /api/skills/{id}/files` defaults to metadata: it is a list endpoint, the CLI is its only caller, and `GET /api/skills` already dropped content for this exact reason (GH #2174). `GET /api/skills/{id}` keeps returning content by default — installed web and desktop builds call it for the skill editor and cannot be retrofitted with a query parameter. The CLI asks for metadata on both and takes `--with-content` when the bodies are actually wanted. The file listing gains a SIZE column, which is what makes an oversized skill diagnosable without downloading it. Co-authored-by: multica-agent <[email protected]> * MUL-6639: fix(cli): fail skill transfers on stall, not elapsed time `http.Client.Timeout` is a wall clock on the whole request, body included, so it punishes exactly the transfer that is working: a 599KB skill arriving steadily over a slow link was cut off at 30s mid-body, while a genuinely dead connection was held open for the same 30s (GH #7498). Raising the constant moves the cliff without removing it — the honest test is not "has this taken too long?" but "is it still making progress?". internal/cli/stall.go adds a client that asks the second question: bounded connect, TLS and response-header phases, and a response body that fails after 15s with no new bytes. The wall clock stays as a loose 10-minute backstop — demoted, not deleted, because dropping it to 0 would have left a stalled request upload with no deadline at all. Failures now say which thing went wrong. A new KindNetworkStalled reports "no data received for 15s after N bytes" rather than a timeout, because only one of those is fixed by waiting longer. Body-read errors are classified at all now: wrapTransport only ever saw errors from http.Client.Do, which returns once the headers arrive, so the failure #7498 actually reports reached users as a raw "context deadline exceeded ... while reading body" out of the JSON decoder. MULTICA_HTTP_STALL_TIMEOUT sets the no-progress budget. An explicitly set MULTICA_HTTP_TIMEOUT still applies on this path, keeping its plain meaning — the longest you will wait for a server — instead of silently reverting to a total-elapsed limit. Adoption starts with the skill commands, where the failure was reported; the mechanism itself is not skill-specific. Graduation is one edit: point NewAPIClient at NewStallAwareHTTPClient, fold StallAwareContext into APIContext, drop newSkillAPIClient. A permanent second client would leave the CLI with two timeout personalities, which is worse than either alone. Co-authored-by: multica-agent <[email protected]> * MUL-6639: fix(skills): hash raw UTF-8, keep the no-include default at content Review found two problems with the previous two commits. **The file hash was not over the file's bytes.** `sha256(content::bytea)` runs the bytea *input* parser over the text rather than taking its bytes, so it reads backslash escapes: a file containing `\x41` hashed as the single byte `A`, disagreeing silently with the Go-side hash of the SKILL.md body. Worse than the mismatch, and not caught earlier because the first check used accented text with no backslash in it: `content::bytea` *errors* on a bare backslash. `C:\Users\...`, a regex `\d+`, a LaTeX snippet — ordinary contents of a skill's supporting files — fail with "invalid input syntax for type bytea", making the listing 500 on the skills most likely to need it. `convert_to(content, 'UTF8')` takes the bytes, and matches Go exactly. **`/api/skills/{id}/files` must not change what a bare request returns.** The previous commit flipped its default to metadata on the grounds that the CLI is its only caller. That was the wrong reading of "only caller": the CLI is installed software too, and an older `skill files list --output json` reads `content`. It is the same argument that kept the detail endpoint's default — applied inconsistently to a client that upgrades separately from the server. Both endpoints now return content when `?include=` is absent. The shrink travels with the caller instead: the CLI sends `include=metadata` itself, so GH #7498 is fixed with no synchronized release, and the default can flip once clients that send `include=content` have aged in. The default is now uniform, so resolveSkillInclude no longer takes a per-endpoint parameter. Tests: TestSkillFileHashCoversRawUTF8Bytes covers `\x41`, invalid hex, bare backslashes, Unicode and empty bodies against the Go hash — verified to fail on the previous SQL. TestSkillEndpointsWithoutIncludeStillReturnContent pins the compatibility guarantee on both endpoints. Not addressed, and noted as follow-up: the metadata path still loads the full SKILL.md into Go, because loadSkillForUser needs the row for its tenant check. The HTTP response no longer grows with content; the database read still does. Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: J <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
71bd844955 |
MUL-6055 refactor(agents): remove legacy agent templates (#6820)
Co-authored-by: Lambda <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
a14098288b | feat: redesign agent Skills and MCP capabilities (#5277) | ||
|
|
81bde585ba |
MUL-3467: batch load squad roster skills (#4386)
* MUL-3467: batch load squad roster skills Co-authored-by: multica-agent <[email protected]> * test: isolate redis-backed test databases Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: J <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
1c91c2a3b2 |
security(db): scope DELETE/UpdateIssueStatus by workspace_id (defense-in-depth) (#3027)
* fix(security): scope DELETE/UpdateIssueStatus by workspace_id Add workspace_id to the WHERE clause of DeleteIssue, DeleteComment, DeleteProject, DeleteSkill, DeleteChatSession, and UpdateIssueStatus as SQL-layer defense-in-depth. Handler loaders (loadIssueForUser / loadSkillForUser / etc.) already enforce workspace membership today, so this is not patching a known live vuln. But the tenant invariant is currently a handler-layer guarantee — a future loader bypass or a new caller skipping the loader would be silently catastrophic. Making workspace_id part of the SQL identity collapses the trust surface to the schema itself: forging a sibling-workspace UUID becomes ErrNoRows instead of a cross-tenant write. Reference: incident #1661 (util.ParseUUID silent zero UUID returning 204 on a DELETE that matched zero rows) — same class of failure, prevented at a different layer. Scope: - 5 DELETE queries: issue, comment, project, skill, chat_session - 1 simple UPDATE: UpdateIssueStatus (2 narg, no SET ordering risk) - All callers updated (handlers, service, runtime sweeper fallback) Multi-narg UPDATE queries (UpdateIssue, UpdateProject, UpdateSkill, UpdateComment, UpdateChatSession*) are deferred to a follow-up to keep this change reviewable: each needs its narg pinning shifted and per-caller verification. sqlc was regenerated by hand (no local sqlc toolchain); CI's backend job is the authoritative compile check. * test(security): add workspace_scope_guard regression test Locks in the SQL-layer tenant guard added in this PR. For each of the 6 scoped queries (DeleteIssue, DeleteComment, DeleteProject, DeleteSkill, DeleteChatSession, UpdateIssueStatus), creates the resource in workspace A, invokes the query with a foreign workspace UUID, and asserts the row is untouched (0 rows affected with no error for :exec; pgx.ErrNoRows for :one). A future refactor that drops the workspace_id arg from any of these queries will now fail loudly instead of silently regressing. Includes a sanity sub-test that the in-workspace path still mutates, so a buggy guard that returns no-op for every call would not pass. Co-Authored-By: Claude Opus 4 <[email protected]> --------- Co-authored-by: Tom Qiao <[email protected]> Co-authored-by: Claude Opus 4 <[email protected]> |
||
|
|
623d29f276 |
feat(agents): one-click create from curated templates (Phase 1) (#2520)
* docs(agents): three-phase agent quick-create plan
Captures the full design for moving agent creation from manual form +
one-by-one skill attachment to a tiered experience:
- Phase 1 (this PR): one-click curated templates, AI-free.
- Phase 2 (next): AI-recommended skills via the existing quick-create
task mechanism — no new server-side LLM dependency.
- Phase 3 (later): AI creates the whole agent end-to-end, composing
Phase 2 with a new `multica agent create` CLI driver.
Documents the architectural decisions that keep all three phases on
existing infrastructure (no SSE, no server-side LLM SDK, no new WS
channels), the two soft blockers Phase 1 unlocks for later phases
(createSkillWithFiles TX composability + skill same-name dedupe), and
the scope decisions we explicitly opted out of (Anthropic plugin
marketplace, ClawHub UI affordances).
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
* fix(skills): harden import against invalid UTF-8 and binary files
PG rejects two byte patterns in a TEXT column. Both crashed real skill
imports we hit while assembling the template catalog:
- Embedded NUL (0x00) -> SQLSTATE 22021. Already stripped by
sanitizeNullBytes, kept as-is.
- Other invalid UTF-8 (e.g. 0x91 — Windows-1252 smart quote in a skill
whose author saved prose from Word). sanitizeNullBytes now also runs
strings.ToValidUTF8 over the content so the second class no longer
takes the whole import down.
For non-text payloads (images, fonts, archives, compiled binaries),
sanitization isn't the right fix — agents never read those as text,
and the bytes can't survive a TEXT column at all. addFile now skips
them by extension before the per-bundle cap counters tick, logging
the skip so an unexpected drop leaves a breadcrumb.
Function name kept for compatibility with the many call sites; both
behaviours are strict supersets of the original.
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
* refactor(skills): split createSkillWithFiles for tx composition + add workspace find-or-create query
Two soft blockers cleared so create-from-template (next commit) can
fold N skill creates and the agent + binding writes into one outer
transaction:
1. createSkillWithFiles used to Begin/Commit its own tx. Caller
composition was impossible — N invocations meant N separate
transactions and no atomicity over the whole materialise step.
Pull the body into createSkillWithFilesInTx(ctx, qtx, input); the
original function becomes a thin wrapper that manages its own tx
for standalone callers. Existing call sites: zero behaviour change.
2. Add GetSkillByWorkspaceAndName sqlc query — workspace skill lookup
by name, anchored to UNIQUE(workspace_id, name) from migration
008. Lets the template materialiser implement find-or-create:
reuse the workspace's existing skill row when a template
references the same name, rather than crashing on the unique
constraint or polluting the workspace with `<name>-2` clones.
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
* feat(agents): agent template catalog + create-from-template endpoint
Server-side foundation for Phase 1 of the quick-create roadmap (see
docs/agent-quick-create-plan.md). Adds:
- server/internal/agenttmpl/ — embed-loaded catalog of curated agent
templates. Each template ships pre-written instructions plus a list
of skill URLs that get materialised into the workspace at create
time. Validation runs at startup (init() panics on a malformed
template) so a bad JSON ships as a deploy-time defect, not a
runtime 500. Slug must equal the filename basename so the URL
router is mirror-symmetric with the file layout.
- 11 starter templates covering Engineering / Writing / Building /
Testing (code-reviewer, frontend-builder, planner, docs-writer,
one-pager, html-slides, full-stack-engineer, …).
- Three new endpoints, all behind RequireWorkspaceMember:
GET /api/agent-templates — picker list (no instructions)
GET /api/agent-templates/:slug — detail with instructions
POST /api/agents/from-template — materialise + create
Create flow:
1. Auth + runtime authorization happen BEFORE the GitHub fan-out
so a 403 never wastes 20s of upstream fetches.
2. Pre-flight dedupe by cached_name reuses workspace skills
without an HTTP fetch — second create-from-the-same-template
drops from 20s to <100ms.
3. Parallel fetch (30s per-URL timeout) for the remaining skills.
4. Single transaction: every skill insert, the agent insert, and
the agent_skill bindings. On any upstream fetch failure the TX
rolls back and the API returns 422 with `failed_urls` so the
UI can name the bad source(s).
5. extra_skill_ids (user-supplied additions) are verified through
GetSkillInWorkspace per id before attach, so a malicious client
can't graft a skill from another workspace via UUID guessing.
- multica agent create --from-template <slug> CLI flag dispatches to
the new endpoint with a 60s ceiling, matching `multica skill import`.
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
* feat(agents): one-click create-from-template UI
Frontend half of Phase 1. CreateAgentDialog becomes a state machine
spanning four steps:
chooser → Start blank / From template cards
blank-form → existing manual form (post-chooser)
duplicate-form → existing form pre-filled from a duplicated agent
template-picker → grid of templates, click navigates to detail
template-detail → instructions + skill list preview + one-click Use
Picking a template never lands on the form: name auto-deduped against
existingAgentNames, runtime = first usable one, visibility = private.
Refinement happens on the agent detail page if needed. Same rationale
the doc spells out — templates exist precisely to skip configuration.
New components, all collapsible-by-default so quick-create stays fast:
- template-picker.tsx — categorised grid, lucide icons + semantic
accent tokens resolved through static maps so Tailwind's JIT picks
up every variant (dynamic class strings would silently miss).
- template-detail.tsx — instructions preview, skill list with cached
descriptions, Use CTA. Renders the failedURLs banner when a 422
fires — the only step that can trigger that response.
- instructions-editor.tsx — collapsed preview-card / expanded full
ContentEditor.
- skill-multi-select.tsx + skill-picker-list.tsx — shared multi-
select surface, also adopted by the existing skill-add-dialog.
- avatar-picker.tsx — agent avatar upload, mirrors the inspector's
visual language.
Schema-defended client (CLAUDE.md → API Response Compatibility): the
three new endpoints are wired through parseWithFallback with lenient
zod schemas. Desktop builds outlive any given server — a future
field rename / wrapping must not white-screen older installs.
listAgentTemplates accepts both the current bare array and a future
{templates: [...]} envelope. Coverage: 7 new schema-test cases in
schema.test.ts (null body, missing skills/instructions, malformed
create response, envelope migration).
Catalog + detail go through TanStack Query with staleTime: Infinity —
workspace-independent static data, no per-mount refetch.
Other:
- skill-add-dialog becomes a true multi-select (Confirm button +
checkbox list); attached skills are filtered out of the list.
- agents-page hands the freshly-created Agent back to the dialog so a
follow-up setAgentSkills can attach the form-selected skills.
- agent-overview-pane drops the mx-auto/max-w-2xl frame on config-
tab content; the wider dialog visual language reads better with
tabs filling the column.
- Every new UI string lives in both en/agents.json and
zh-Hans/agents.json under create_dialog.* / tab_body.skills.* —
locales/parity.test.ts blocks drift in CI.
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
* fix(ci): align skill import test + drop next-only lint suppression
- TestFetchFromSkillsSh_ResolvesRootLevelSkillMd now expects assets/logo.png
to be skipped; matches the new addFile binary-extension guard
(
|
||
|
|
d0ac67dea2 |
fix(skills): drop SKILL.md content from list endpoints (#2180)
* fix(skills): drop SKILL.md content from list endpoints (#2174) `GET /api/skills` and `GET /api/agents/{id}/skills` were SELECT *'ing the skill row and shipping the full SKILL.md `content` blob to every caller. SKILL.md bodies routinely run 50–200KB each, so a workspace with 30–40 skills returned multi-megabyte JSON arrays — past the CLI's 15s timeout on high-latency links and locking out non-US users entirely. Add `ListSkillSummariesByWorkspace` / `ListAgentSkillSummaries` sqlc queries that omit `content`, plus a dedicated `SkillSummaryResponse` wire shape so the contract is explicit (versus stuffing `Content: ""` back into the existing struct). Detail endpoints (`GET /api/skills/{id}`, agent CRUD return values) keep returning the full body. `AgentResponse.skills` and the matching TS `Agent.skills` now use `SkillSummary[]` — frontend list/columns code already only read id/name/description/config.origin, so the type narrowing matches actual usage and prevents new code from accidentally depending on a content field that won't be there. Co-authored-by: multica-agent <[email protected]> * fix(agents): narrow embedded skills to AgentSkillSummary; gofmt agent.go GPT-Boy review of #2180: the previous commit typed AgentResponse.Skills as []SkillSummaryResponse, but the agent list batch query (ListAgentSkillsByWorkspace) only joins agent_id/id/name/description, so the wider type left workspace_id/config/created_at/updated_at as zero values. Define a dedicated AgentSkillSummary {id,name,description} that matches what the batch query actually returns and what the frontend actually reads (`agent.skills.map(s => s.name|s.id)`); the standalone GET /api/agents/{id}/skills endpoint keeps SkillSummaryResponse for callers that need the source/origin info. Switch GetAgent's per-agent skills load from ListAgentSkills (full Skill rows including content) back to ListAgentSkillSummaries to avoid reading SKILL.md bodies just to discard them. Re-run gofmt on agent.go to fix the field-tag alignment that drifted when Skills changed type. Co-authored-by: multica-agent <[email protected]> * docs(types): correct SkillSummary JSDoc — Agent.skills is AgentSkillSummary[] GPT-Boy spotted on review: comment said SkillSummary was "embedded in Agent.skills", but that field is now AgentSkillSummary[]. Re-point the reader at the right type to avoid future confusion. Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: multica-agent <[email protected]> |
||
|
|
9ede795c5b |
feat(api): strict workspace isolation + agent parity fixes
Enforce workspace isolation at every layer: - Router: move RequireWorkspaceMember middleware to group level so ALL workspace-scoped routes (issues, agents, skills, runtimes, inbox, comments) require workspace context - SQL: add GetXxxInWorkspace queries that filter by workspace_id, eliminating cross-workspace data access at the query level - Handlers: loadXForUser functions use workspace-scoped queries, no fallback to unscoped queries - Migration 025: add workspace_id column to comment table with backfill - ListComments: add workspace_id filter for defense-in-depth Fix daemon workspace mapping: - Server returns workspace_id in task claim response (from issue) - Daemon uses task.WorkspaceID directly instead of unreliable workspaceIDForRuntime() local map lookup - Remove workspaceIDForRuntime function Fix agent/human parity: - Comment update/delete: use resolveActor for isAuthor check so agents can edit/delete their own comments - Event attribution: replace hardcoded "member" with resolveActor in agent, skill, and subscriber publish calls Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> |
||
|
|
02df33803a |
feat: structured skills system with meta skill runtime injection
Replace agent.skills TEXT field with structured skill/skill_file/agent_skill tables. Skills are workspace-level entities with supporting files, reusable across agents via many-to-many bindings. Backend: migration 008, sqlc queries, CRUD handler, agent-skill junction, structured skill loading in task context snapshot. Daemon: meta skill injection via runtime-native config (.claude/CLAUDE.md for Claude, AGENTS.md for Codex) so agents discover .agent_context/ skills through their native mechanism. Lean prompt without inlined skill content. Frontend: Skills management page, agent Skills tab picker, SDK methods, TypeScript types, workspace store integration. Also removes auto-creation of init issues when creating agents. Co-Authored-By: Claude Opus 4.6 <[email protected]> |