Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
20677ce
refactor(oauth): move config and model-alias shapes out of the provid…
elkaix Aug 24, 2026
9ea86f7
refactor(agent-core-v2): stop generating session titles on a hosted e…
elkaix Aug 24, 2026
083edaf
feat(plugins): remove the bundled datasource plugin
elkaix Aug 24, 2026
361213d
refactor(agent-core-v2): stop routing plugin and web credentials thro…
elkaix Aug 24, 2026
25ce838
docs: correct agent lifecycle scope guidance
elkaix Aug 24, 2026
d858cf5
fix(agent-core-v2): honor secondary model thinking effort
elkaix Aug 24, 2026
5f22564
refactor(agent-gateway): delegate file watching to workspaces
elkaix Aug 24, 2026
08dded4
fix(transcript): reconcile live and cold session state
elkaix Aug 24, 2026
732b65d
fix(agent-gateway): report title generation as unavailable
elkaix Aug 24, 2026
c27cde3
feat(agent-core-v2): add MCP management core
elkaix Aug 24, 2026
933250c
fix(agent-core): align MCP management RPC contracts
elkaix Aug 24, 2026
0d70d97
feat(agent-gateway): add gated MCP management API
elkaix Aug 24, 2026
25684dd
feat(klient): add gated MCP management facade
elkaix Aug 24, 2026
eb2dff6
feat(node-sdk): delegate MCP management to engine
elkaix Aug 24, 2026
e2ffb28
refactor(agent-core-v2): migrate features to agent runtimes
elkaix Aug 24, 2026
789ccb3
refactor: adapt consumers to agent runtimes
elkaix Aug 24, 2026
442947a
docs: document recent CLI and MCP behavior
elkaix Aug 24, 2026
6fe808d
build: update Nix dependency hash
elkaix Aug 24, 2026
1c3f447
feat(web): refine composer and session interactions
elkaix Aug 24, 2026
a17f240
refactor(oauth): remove managed provider support
elkaix Aug 24, 2026
6f22c03
refactor(cli): remove managed provider workflows
elkaix Aug 24, 2026
0f1073d
refactor(web): remove managed provider flows
elkaix Aug 24, 2026
043ec3c
refactor(acp): remove managed login methods
elkaix Aug 24, 2026
ab4a5b4
refactor(agent): remove managed provider contracts
elkaix Aug 24, 2026
777116a
docs: remove managed provider guidance
elkaix Aug 24, 2026
18f7895
test: enforce product boundaries
elkaix Aug 24, 2026
176c122
refactor(agent-core-v2): move skills to agent runtimes
elkaix Aug 24, 2026
999eb53
feat(agent-core-v2): add dynamic workflow timeout
elkaix Aug 24, 2026
2a86eac
feat(agent-core-v2): track turn-level tool repeats
elkaix Aug 24, 2026
dbd5f25
fix(tower): remove coordinator command queue
elkaix Aug 24, 2026
4e78a96
feat(agent-core-v2): expose the Tower experiment flag
elkaix Aug 24, 2026
3762c04
fix(tui): preserve active session after provider logout
elkaix Aug 24, 2026
179b296
feat(fs): add workspace-independent multi-root suggestions
elkaix Aug 24, 2026
6bfeccb
refactor(agent-core-v2): keep system prompts immutable
elkaix Aug 24, 2026
6e60484
chore: remove stale provider cleanup imports
elkaix Aug 24, 2026
0c3ef3c
fix(oauth): remove the deleted device build entry
elkaix Aug 24, 2026
9e66c24
fix(oauth): sync the lockfile after managed removal
elkaix Aug 24, 2026
ad8e30e
chore: remove trailing whitespace
elkaix Aug 24, 2026
23fc82c
chore: remove internal-only wording
elkaix Aug 24, 2026
17df136
chore(web): preserve generated bundle output
elkaix Aug 24, 2026
77e26d7
test(tower): allow submodule cleanup under suite load
elkaix Aug 24, 2026
084126c
chore: add release changesets
elkaix Aug 24, 2026
8f8ec57
docs: align plugin and theme references
elkaix Aug 24, 2026
4e2294f
fix(agent-core-v2): expire MCP auth flows by host clock
elkaix Aug 24, 2026
57bfa50
test(agent-core-v2): remove timing-dependent waits
elkaix Aug 24, 2026
6c00507
test(gateway): await complete search indexing
elkaix Aug 24, 2026
575e501
fix: harden security-sensitive request handling
elkaix Aug 24, 2026
d717646
build: refresh generated dependency hashes
elkaix Aug 24, 2026
57688e5
fix: close remaining security data flows
elkaix Aug 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .agents/skills/agent-core-dev/SKILL.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,7 @@ End-to-end procedures that span the stages. Reach for these before reading the s

## Stages

- [Stage 1 — Orient](orient.md): the DI black box (identity / dependencies / lifetime), the four `LifecycleScope` tiers and visibility, and the no-comment convention. Read before touching business code.
- [Stage 1 — Orient](orient.md): the DI black box (identity / dependencies / lifetime), the three `LifecycleScope` tiers and visibility, the separate workspace program lifetime, and the no-comment convention. Read before touching business code.
- [Stage 2 — Design a service](design.md): pick a scope, split a domain across scopes, choose a calling style (direct call vs event vs hook), and direct dependencies. Decide *where things live and who knows whom* before coding.
- Topic: [Domain boundaries vs Scope](domain-boundaries.md) — keep `session` / `agent` / `turn` from becoming god objects; data-ownership test and their split conclusions.
- Topic: [Persistence layering](persistence.md) — the three-layer `Store → Storage → backend` model, naming Stores by access pattern, and which layer business code should depend on.
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/agent-core-dev/align.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,7 +14,7 @@ v1 is a **VSCode-style singleton container**: services self-register with `regis
|---|---|---|
| Registration | `registerSingleton(IX, X, InstantiationType.Delayed)` | `registerScopedService(LifecycleScope.X, IX, X, ScopeActivation.OnDemand, 'domain')` |
| DI import | `from '../../di'` | `from '#/_base/di/scope'` / `'#/_base/di/instantiation'` / `'#/_base/di/lifecycle'` |
| Lifetime | implicit singleton-per-container | explicit `LifecycleScope` (App/Workspace/Session/Agent) — see orient.md |
| Lifetime | implicit singleton-per-container | explicit `LifecycleScope` (App/Session/Agent); workspace resources use the separate `WorkspaceInstance` / `Program` lifetime — see orient.md |
| Domain granularity | coarse (`session`, `tool`, `loop`) | fine, split by scope + responsibility |
| Test import | `from '@pymodel/agent-core/di/test'` | `from '#/_base/di/test'` |
| Resolve SUT in tests | `ix.createInstance(Impl)` (common) | `ix.get(IX)` by interface — see test.md |
Expand Down
80 changes: 29 additions & 51 deletions .agents/skills/agent-core-dev/design.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,12 +17,12 @@ A Service = a bundle of **state** + a set of **behaviors**, bound to a **lifetim

> Scope = the identity + lifetime of the owned state.

| Scope | State identity (keyed by) | Lifetime |
| Owner | State identity (keyed by) | Lifetime |
|---|---|---|
| `App` | none (single global instance) | the process |
| `Workspace` | `workspaceId` | one workspace handler (materialized once per workspace, never closed — dies with the process) |
| `Session` | `sessionId` | one session |
| `Agent` | `agentId` | one agent |
| `LifecycleScope.App` | none (single global instance) | the process |
| workspace `Program` | `workspaceId` | one materialized workspace instance or runtime generation |
| `LifecycleScope.Session` | `sessionId` | one session |
| `LifecycleScope.Agent` | `agentId` | one agent |

### Decision tree

Expand All@@ -34,7 +34,7 @@ A Service = a bundle of **state** + a set of **behaviors**, bound to a **lifetim
**Q2. What is the identity of that state?**

- one global instance → **`App`**
- one per workspace (shared by every session of that workspace) → **`Workspace`**
- one per workspace (shared by every session of that workspace) → a program-owned **workspace component**
- one per session → **`Session`**
- one per agent → **`Agent`**
- a mix (a global registry *and* per-instance state) → **split it** (see §3).
Expand DownExpand Up@@ -72,7 +72,7 @@ The standard split is "global registry / factory" + "per-instance":
| Tier | Role | Naming tends to |
|---|---|---|
| `App` | global registry / catalog / factory — knows "all of them" and how to create one | `XxxStore` / `XxxRegistry` / `XxxCatalog` |
| `Workspace` / `Session` / `Agent` | one instance — only the state of "this one" | `XxxService` / `IWorkspaceXxx` / `ISessionXxx` / `IAgentXxx` |
| workspace program / `Session` / `Agent` | one instance — only the state of "this one" | `XxxService` / `IWorkspaceXxx` / `ISessionXxx` / `IAgentXxx` |

Canonical splits in the codebase:

Expand DownExpand Up@@ -179,13 +179,13 @@ Two standing red lines on top of that:
After the checklist, render the result as a plaintext tree — the deliverable reviewers read. Keep it in the design doc or PR description.

```text
domain: `<name>` (owning scope: <Scope>)
domain: `<name>` (owning lifetime: <Owner>)
├─ serves (who uses me) tag = HOW they reach me
│ ├─ (inject) <ConsumerDomain> @<Scope> — <what they use me for>
│ └─ (accessor) <ConsumerDomain> @<Scope> — <what they use me for>
├─ exposes (interfaces I provide, by scope)
│ ├─ App : <IXxxRegistry> — <role>
│ ├─ Workspace : <IWorkspaceXxx> — <role>
│ ├─ Workspace program : <IWorkspaceXxx> — <role>
│ ├─ Session : <ISessionXxx> — <role>
│ └─ Agent : <IAgentXxx> — <role>
└─ depends (what I inject) tag = calling style
Expand DownExpand Up@@ -225,52 +225,30 @@ Read it as:
Worked example — `sessionLifecycle`:

```text
domain: `sessionLifecycle` (owning scope: Workspace)
├─ serves (who uses me)
│ ├─ (inject) — (none)
│ └─ (accessor)
│ ├─ sessionLegacy @App(edge) — v1-compatible create/fork/archive/…
│ └─ gateway / rpc @App(edge) — native v2 session lifecycle actions
├─ exposes (interfaces I provide, by scope)
│ ├─ Workspace : ISessionLifecycleService — owns this workspace's live session scope tree
│ ├─ Session : — — (per-session state lives in sessionMetadata / agentLifecycle / …)
│ └─ Agent : — — (per-agent state lives in agentLifecycle)
└─ depends (what I inject)
├─ workspaceContext @Workspace seed — handler identity + persistence scope
├─ bootstrap @App direct — addresses session storage
├─ hostEnvironment @App direct — gates scope creation on the probe
├─ sessionIndex @App direct — persisted read model for cold resumes
├─ storage @App direct — atomic docs + append logs
├─ workspaceDirs / workspaceSkillCatalog / workspaceMcp / …
│ @Workspace direct — the handler's shared resource services
└─ event @App direct — broadcasts session-level facts (e.g. archived)
```

Cross-scope borrow for `sessionLifecycle`:

```text
App scope
WorkspaceLifecycleService ──holds──► IScopeHandle(workspaceId) (one per live handler)
│ accessor.get(ISessionLifecycleService)
│ └── resolve runs inside the Workspace scope
Workspace scope (workspaceId)
SessionLifecycleService ──holds──► IScopeHandle(sessionId)
│ accessor.get(ISessionMetadata) …
│ └── resolve runs inside the Session scope
Session scope (sessionId)
sessionMetadata / agentLifecycle / … ← per-session services live here
domain: `sessionLifecycle` (owning lifetime: workspace Program)
├─ serves
│ └─ SessionManager @App — creates, resumes, forks, closes, and archives sessions
├─ exposes
│ └─ workspace Program : ISessionLifecycleService — owns this controller's live Session scopes
└─ depends
├─ workspace context/resources @Program direct — identity, fs, dirs, skills, MCP, profiles
└─ App services @App direct — persistence, config, telemetry, events
```

How the three lenses shaped it:
The App-scoped `IWorkspaceInstanceManager` owns `WorkspaceInstance` objects. Each instance owns a
`Program`. The Program constructs workspace resources and creates a `SessionLifecycleService`
with them. That service creates real `LifecycleScope.Session` children, and each session creates
`LifecycleScope.Agent` children. There is no workspace `IScopeHandle` and no Workspace value in
`LifecycleScope`.

- **Scope (§2)** → the live registry of one workspace's session scopes is per-handler, so it is Workspace-scoped; the process-wide handler registry lives in the App-scoped `workspaceLifecycle`; per-session data stays in Session-scoped services, reached through the handle's `accessor`.
- **Dependency direction (§5)** → `sessionLifecycle` is consumed by the edge via `accessor` borrows; it never imports the edge. Every downward arrow lands on a peer or a more foundational Service.
- **Extension points (§4)** → new per-session behavior plugs into the Session-scoped services (`sessionMetadata`, `agentLifecycle`, `sessionActivity`); new transports stay at the edge. Neither edits `sessionLifecycle`.
How the three lenses shape it:

- **Lifetime (§2)** → workspace state belongs to the Program; per-session state belongs to Session
scopes; per-agent state belongs to Agent scopes.
- **Dependency direction (§5)** → the Program receives App dependencies and passes workspace
resources into the session controller; business code does not import the edge.
- **Extension points (§4)** → new per-session behavior belongs in Session-scoped services; new
transports stay at the edge.
For a multi-scope split, the `exposes` block fills more than one scope — see the `records` pattern in §3.

## Red lines (this stage)
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/agent-core-dev/domain-boundaries.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -82,7 +82,7 @@ The `session` domain owns only Session-level identity, metadata, lifecycle comma
|---|---|---|
| `sessionId`, `workspaceId`, `sessionDir`, `metaScope` | `sessionContext` | Seeded facts; no IO |
| `SessionMeta` | `sessionMetadata` | Durable atomic document; entity-like |
| Open session scope registry | `sessionLifecycle` | Workspace-scope live handles, one registry per workspace handler (the process-wide handler registry is `workspaceLifecycle`); not the persisted entity table |
| Open session scope registry | `sessionLifecycle` | Program-owned controller with live Session handles; each workspace Program creates it from current workspace resources; not the persisted entity table |
| Session commands such as `archive()` | `session` | Orchestrates metadata, agent teardown, and events |
| Persisted session list / get / count | `sessionIndex` | Backend-neutral read model |
| Running / idle / awaiting status | `sessionActivity` | Derived from interactions and active turns; owns no state |
Expand Down
6 changes: 3 additions & 3 deletions .agents/skills/agent-core-dev/edge-exposure.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,11 +13,11 @@ The transport (`/api/v2` over HTTP + WS) lives in the **edge** layer (`gateway`/

## 1. The edge model

Four scopes, four URL shapes, one dispatcher:
Three DI scopes, four resource address shapes:

```text
GET|POST /api/v2/:sa Core
GET|POST /api/v2/workspace/:workspace_id/:sa Workspace
GET|POST /api/v2/workspace/:workspace_id/:sa Workspace program
GET|POST /api/v2/session/:session_id/:sa Session
GET|POST /api/v2/session/:session_id/agent/:agent_id/:sa Agent
```
Expand All@@ -29,7 +29,7 @@ GET|POST /api/v2/session/:session_id/agent/:agent_id/:sa Agent
- `:action` is the method. `GET` for reads, `POST` for writes.
- Body = the method's single argument (JSON), omitted for no-arg.
- Response = the project envelope `{ code, msg, data, request_id, details? }`.
- The dispatcher resolves the **scope** from the URL, the **Service** from an `actionMap`, calls the method, wraps the result.
- The original dispatcher design resolves an address from the URL, selects a Service from an `actionMap`, calls the method, and wraps the result. A workspace URL identifies a program-owned resource; Workspace is not a `LifecycleScope` value.

```ts
// actionMap — the allowlist; hides internal domain names.
Expand Down
20 changes: 10 additions & 10 deletions .agents/skills/agent-core-dev/orient.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,28 +12,28 @@ When writing business code you declare three things; the container handles the r

Classes talk only to interfaces and never care how an implementation is constructed.

## The four `LifecycleScope` tiers
## The three `LifecycleScope` tiers

Lifetimes form a tree, from longest to shortest:
DI lifetimes form a tree, from longest to shortest:

```text
App process-wide, single global instance
└── Workspace one workspace handler (a materialized workspace root)
└── Session one session
└── Agent one agent
└── Session one session
└── Agent one agent
```

```ts
// src/app/scopes.ts — the business layer declares the tiers and their order;
// the DI kernel only knows opaque string kinds plus the declared topology.
export enum LifecycleScope {
App = 'app',
Workspace = 'workspace',
Session = 'session',
Agent = 'agent',
}
```

Workspace resources have a separate lifetime. The App-scoped `IWorkspaceInstanceManager`
materializes one `WorkspaceInstance` per workspace. Its `Program` constructs and disposes the
workspace services. `Workspace` is a domain identity, not a `LifecycleScope` value.

- Later in the topology = shorter life = closer to a leaf.
- "Singleton" means **one per scope**: `ILogService` is global once; each `Session` scope has its own `ISessionMetadata`.
- `kind` must advance along the declared topology in the parent→child direction.
Expand All@@ -49,7 +49,7 @@ A child scope sees its ancestors; a parent never sees its children. Resolution w

### Disposal order

Deterministic: **child scopes die first; within one scope, teardown runs in strict reverse registration order, one entry at a time.** The mechanism is the Ledger (`src/_base/lifecycle/`): ordered effect bookkeeping, dual-track (sync + async disposers), serial reverse-order teardown (never parallel), with the teardown reason (`'scope-close' | 'cascade' | 'unload'`) passed through to every disposer. `Disposable` / `DisposableStore` (`src/_base/di/lifecycle.ts`) delegate to it — "reverse construction order" is a Ledger property, not a container convention. Business code declares which tier it lives in and never disposes by hand.
Deterministic: **child scopes die first; within one scope, teardown runs in strict reverse registration order, one entry at a time.** The mechanism is the Ledger (`src/_base/lifecycle/`): ordered effect bookkeeping, dual-track (sync + async disposers), serial reverse-order teardown (never parallel), with the teardown reason (`'scope-close' | 'cascade' | 'unload'`) passed through to every disposer. `Disposable` / `DisposableStore` (`src/_base/di/lifecycle.ts`) delegate to it — "reverse construction order" is a Ledger property, not a container convention. Scoped business code declares its DI tier. Workspace programs explicitly own their manually constructed resources.

## Dynamic DI: units and cascades

Expand All@@ -68,7 +68,7 @@ There is no domain-layer numbering — a domain may import any other domain, gui

## Comment convention

`packages/agent-core-v2/AGENTS.md` bans comments: no file headers, no section banners, no statement-level narration — the code is the source of truth. The only exception is JSDoc attached to exported symbols, which flows into the generated `.d.ts` and the consumers' IDE hover. Tooling directives (`eslint-disable`, `@ts-expect-error`, …) are banned too: fix the underlying lint/type problem instead, and put negative type-safety cases in compiler-asserted fixtures. Scope is carried by the filename: `workspace*.ts` = Workspace, `session*.ts` = Session, `agent*.ts` = Agent, no prefix = App (see service-authoring.md).
`packages/agent-core-v2/AGENTS.md` bans comments: no file headers, no section banners, no statement-level narration — the code is the source of truth. The only exception is JSDoc attached to exported symbols, which flows into the generated `.d.ts` and the consumers' IDE hover. Tooling directives (`eslint-disable`, `@ts-expect-error`, …) are banned too: fix the underlying lint/type problem instead, and put negative type-safety cases in compiler-asserted fixtures. DI scope is carried by registration: `LifecycleScope.App`, `LifecycleScope.Session`, or `LifecycleScope.Agent`. A `workspace*` filename marks workspace-domain ownership, not a DI scope (see service-authoring.md).

## Red lines (this stage)

Expand Down
Loading
Loading