Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,17 @@ Discovery result: the inspected provider exposes a stateful chat/resource API pl
**Files:**
- Modify this plan or add contract notes if the decision is made before implementation.

- [ ] **Step 1: Choose package boundary**
- [x] **Step 1: Choose package boundary**

Pick one:

- new published adapter package, if consumers should import it directly;
- private/internal adapter, if this is only a migration bridge;
- no code, if provider APIs are unstable or unavailable.

- [ ] **Step 2: Decide public API shape**
Decision: add a private/internal adapter proof under `libs/chat` first. Do not create a new published package or public export until the package boundary is approved.

- [x] **Step 2: Decide public API shape**

If public, mirror existing adapter ergonomics:

Expand All @@ -132,7 +134,9 @@ injectAgent(ref?)

Use a provider-specific prefix internally if needed to avoid collisions with existing adapter types, but keep the consumer mental model aligned with AG-UI and LangGraph.

- [ ] **Step 3: Decide client-tools mapping**
Decision: no public API in the adapter-proof PR. A future public package should mirror `toAgent(source, options?)` and DI provider ergonomics only after API shape review.

- [x] **Step 3: Decide client-tools mapping**

Confirm whether provider tools are:

Expand All @@ -142,13 +146,15 @@ Confirm whether provider tools are:

Only map browser-executed tools into Threadplane `action`/`view`/`ask` when the provider supplies stable pending-call identifiers and a result-return API.

Decision: do not map provider tools into `ClientToolsCapability` in the adapter-proof PR. The inspected source exposes provider-owned tool execution without a stable browser result-return API for Threadplane client tools.

## Task 3: Contract Tests First

**Files:**
- Add: adapter conformance spec path TBD.
- Add: provider fake/test fixture path TBD.

- [ ] **Step 1: Build a scriptable provider fake**
- [x] **Step 1: Build a scriptable provider fake**

Create a test-only fake from the captured event contract. It must support:

Expand All @@ -157,7 +163,9 @@ Create a test-only fake from the captured event contract. It must support:
- message/tool history inspection;
- branch-on-tool-result behavior if the provider supports continuation.

- [ ] **Step 2: Write failing `Agent` conformance tests**
Implementation note: the first adapter-proof PR uses a scriptable signal-resource fake scoped to the observed public resource surface, not transport-frame scripts.

- [x] **Step 2: Write failing `Agent` conformance tests**

Cover:

Expand All @@ -171,7 +179,7 @@ Cover:
- regenerate trims at the selected assistant message and reruns;
- custom events flow through `events$` or adapter-specific signal if supported.

- [ ] **Step 3: Write client-tools bridge tests only if supported**
- [x] **Step 3: Write client-tools bridge tests only if supported**

If provider has stable browser tool primitives, cover:

Expand All @@ -184,16 +192,20 @@ If provider has stable browser tool primitives, cover:

Do not implement client-tools mapping if the provider lacks stable tool-call IDs or result APIs.

Decision: not supported for this proof. The adapter tests cover tool-call projection only; no client-tools bridge tests are added.

## Task 4: Minimal Adapter Implementation

**Files:**
- Add/modify only after Tasks 1-3 pass their decision gates.

- [ ] **Step 1: Implement event reduction**
- [x] **Step 1: Implement event reduction**

Prefer the AG-UI reducer path if compatible. Otherwise implement only the reducer cases proven by fixtures. Unknown events must be ignored or surfaced as neutral custom events; they must not crash the stream.

- [ ] **Step 2: Implement actions**
Implementation note: the proof uses resource-state projection rather than event reduction because the inspected public API exposes resource state, not a public event subscriber.

- [x] **Step 2: Implement actions**

Map:

Expand All @@ -204,11 +216,13 @@ Map:

to provider APIs only where semantics match the existing `Agent` contract. Throwing is acceptable for programmer misuse, but normal unsupported provider features should be absent or no-op only if that matches existing adapter precedent.

- [ ] **Step 3: Add Angular provider wiring**
- [x] **Step 3: Add Angular provider wiring**

If public or DI-supported, mirror the existing typed `AgentRef` overloads and factory config pattern from AG-UI/LangGraph.

- [ ] **Step 4: Add public exports and docs only after approval**
Decision: not applicable to the private proof. No DI/provider helper is added.

- [x] **Step 4: Add public exports and docs only after approval**

If the adapter is public:

Expand All @@ -218,16 +232,24 @@ npm run generate-api-docs

Expected: generated API docs include the new bridge types. Commit generated docs with the public export.

Decision: not applicable to the private proof. No public export is added, so API docs are unchanged.

## Task 5: Verification

**Files:**
- No additional files.

- [ ] **Step 1: Run focused adapter tests**
- [x] **Step 1: Run focused adapter tests**

Run the new adapter test target or focused Vitest spec.

- [ ] **Step 2: Run affected existing adapter/chat tests**
Verified:

```bash
NX_DAEMON=false npx nx test chat --skip-nx-cache --outputStyle=static --testFile=src/lib/agent/signal-chat-resource-agent.spec.ts
```

- [x] **Step 2: Run affected existing adapter/chat tests**

Run:

Expand All @@ -239,18 +261,37 @@ NX_DAEMON=false npx nx test chat --skip-nx-cache --outputStyle=static

Expected: all pass. If the new bridge is in its own project, run its test/lint/build target as well.

- [ ] **Step 3: Lint and build affected projects**
Verified:

```bash
NX_DAEMON=false npx nx test ag-ui --skip-nx-cache --outputStyle=static
NX_DAEMON=false npx nx test langgraph --skip-nx-cache --outputStyle=static
NX_DAEMON=false npx nx test chat --skip-nx-cache --outputStyle=static
```

- [x] **Step 3: Lint and build affected projects**

Run the smallest affected Nx lint/build targets. If API docs changed, verify generated docs are committed.

- [ ] **Step 4: Forbidden-reference scan**
Verified:

```bash
NX_DAEMON=false npx nx lint chat --skip-nx-cache --outputStyle=static
NX_DAEMON=false npx nx build chat --skip-nx-cache --outputStyle=static
```

- [x] **Step 4: Forbidden-reference scan**

Before committing code, run a staged diff scan excluding `docs/superpowers/**` and confirm no forbidden external framework names appear in code, comments, commit text, or PR text.

- [ ] **Step 5: Diff audit**
Verified with a staged diff scan excluding `docs/superpowers/**`; no forbidden references appeared in code or comments.

- [x] **Step 5: Diff audit**

Confirm the diff contains no unrelated refactors, no package dependency changes unless explicitly approved, and no behavior changes to existing AG-UI or LangGraph adapters.

Verified: diff is limited to the private adapter proof, its tests, and this plan status update.

---

## Current Recommendation
Expand Down
8 changes: 7 additions & 1 deletion examples/chat/angular/e2e/test-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,19 @@ export function attachBrowserHygiene(page: Page): {
page.on('requestfailed', (request) => {
const failure = request.failure()?.errorText ?? '';
const url = request.url();
if (/runs\/stream/.test(url) && /abort|ERR_ABORTED/i.test(failure)) return;
if (isBenignAbortedRequest(url, failure)) return;
failedRequests.push(`${request.method()} ${url} ${failure}`.trim());
});

return { consoleErrors, failedRequests };
}

function isBenignAbortedRequest(url: string, failure: string): boolean {
if (!/abort|ERR_ABORTED/i.test(failure)) return false;
if (/runs\/stream/.test(url)) return true;
return /fonts\.gstatic\.com\/s\/materialsymbolsoutlined\/.+\.woff2/.test(url);
}

export async function openDemo(page: Page, path = '/embed'): Promise<void> {
await page.goto(path);
await page.evaluate(() => {
Expand Down
Loading
Loading