Skip to content

refactor(agent): cut the agent's outside edges and fence them - #1297

Merged
gewenyu99 merged 17 commits into
posthog/functional-a1-minfrom
posthog/functional-a2a-edges
Sep 23, 2026
Merged

gewenyu99 merged 17 commits into
posthog/functional-a1-minfrom
posthog/functional-a2a-edges

Conversation

@gewenyu99

@gewenyu99 gewenyu99 commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #1293 (A1′). First of the two PRs that replace the plan's A2′: this one cuts every import the agent still made outside itself and the shared set, then fences them with ESLint. The follow-up moves the files to src/agent and src/shared with no logic in it.

Intent

Cuts every import the agent still makes into the UI, the session, detection, the programs or the CLI, and makes ESLint refuse new ones. No behavior change: the same lines reach the terminal, now as progress events the legacy adapter maps back to getUI().

Next PR (A2b) moves src/lib/agent to src/agent and the shared modules to src/shared. Pure renames, no logic, reviewable with git diff -M.

Files

Behavior changed, read these.

Threading emit only: agent-interface.ts, index.ts, task.ts, linear.ts, index.ts, types.ts.

Tests, each failed first with @ui mocked to throw: benchmark-emit.test.ts, yara-flush-report.test.ts, handoff.test.ts, agent-progress.test.ts, progress-collector.test.ts.

Plumbing. A definition moves, the old path re-exports it, typecheck proves it. New homes first, then the re-exporting owners.

Everything else in the diff is an import line following one of those moves, plus known-violations.json regenerated (78 → 73).

Review by commit

One edge per commit. The first seven are moves with re-exports; typecheck proves them. The last four change behavior and each has a test that failed first with @ui mocked to throw.

Commit Edge Read
3be61a86 WizardError → errors/ new file, re-export
9ce78997 progress payload types, AgentChunk → agent progress.ts, wizard-ui.ts
f15dc7a4 outro/question/notice shapes → agent, Credentials/AdditionalFeature → shared progress.ts, wizard-session.ts head
53bddf83 ProgramRun split program-run.ts, types.ts; 14 programs swap one import
54f8e4be authenticate → programs git diff -M rename
85ed50e4 document names + audit ledger → shared leaves audit-ledger.ts, constants.ts, yara-hooks.ts imports
a88c5816 Node package-manager detector → utils utils/package-manager.ts tail
0b98bd37 middleware through emit benchmark.ts, summary.ts, benchmark-emit.test.ts
856b613d yara flush returns the line flushScanReport, runner/index.ts finally
13fa0a28 handoff progress event handoff.ts, progress.ts, agent-progress.ts
5cffae63 ESLint fence .eslintrc.cjs override

Verification

Full notes and numbers: workbench/wizard-functional-evidence/a2a-edges-evidence.md.

  • pnpm typecheck, pnpm lint (0 errors), pnpm build:ci pass.
  • pnpm vitest run: 191 files, 3161 tests, 5 new. No golden regenerated.
  • Architecture data file 78 → 73 known violations. The only additions are type re-export edges (wizard-session, wizard-ui, wizard-ask-bridge, handoff, program-run → agent).
  • Headless --ci on a fresh express-todo copy: exit 0, PostHog set up: 7/7 steps completed (1 skipped as not required), app instrumented (index.js, posthog.js, package.json, package-lock.json). Log: workbench/wizard-functional-evidence/a2a-ci-run-express-todo.log.
Fence probe: six imports in a scratch file under src/lib/agent, linted once
fence-probe.ts:1:1: '@ui' import is restricted from being used by a pattern. The agent reports through progress events and asks through AgentInteraction; it takes everything else through RunConfig and RunInput.
fence-probe.ts:2:1: '@lib/programs/program-registry' import is restricted from being used by a pattern. The agent takes program data through RunConfig. Types only, until B1 moves PROGRAM_BINDINGS to programs.
fence-probe.ts:4:1: '../wizard-session' import is restricted from being used by a pattern. …
fence-probe.ts:5:10: 'wizardAbort' import from '@utils/wizard-abort' is restricted. The agent never exits the process: return a failure in RunResult.
4 problems

import type { ProgramId } and registerCleanup passed.

Headless run tail
◇  Reviewing the reporting and notebook workflows before assembling the recorded integration outcome.
◌  Reviewing the reporting and notebook workflows before assembling the recorded integration outcome.
◇  Publishing the recorded setup report, then mirroring the identical content into a shareable notebook.
◌  Publishing the recorded setup report, then mirroring the identical content into a shareable notebook.
◇  Recording the completed reporting handoff for the next workflow step.
◌  Recording the completed reporting handoff for the next workflow step.
└  PostHog set up: 7/7 steps completed (1 skipped as not required).
exit=0

Created with PostHog Desktop

The agent builds WizardError for every decided failure, and it had to
import the process-exit module to do so. The class now lives in
src/lib/errors/wizard-error.ts; wizard-abort re-exports it so every
other importer is unchanged.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
TokenUsageDelta, SpinnerHandle and AuthErrorDetail are what the agent
puts on its progress events, and AgentChunk is what the streaming prompt
runner yields. They move next to the code that produces them;
wizard-ui.ts and the MCP prompts service re-export them so every UI
importer keeps its path. The agent no longer imports from src/ui for
these.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
OutroKind, OutroData, AskQuestion, AskAnswers, PendingQuestion and
TaskNotice are what runAgent returns and asks with, so they move into
src/lib/agent/progress.ts. Credentials moves next to the API types,
AdditionalFeature next to the other program enums in constants, and the
session's CloudRegion copy points at the one in @utils/types.
wizard-session.ts re-exports all of them, so its 127 importers are
unchanged. The agent no longer imports @lib/wizard-session for a shape;
the one remaining WizardSession reference is ProgramRun's session hooks.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
…e program hooks

The agent's RunConfig.run is now AgentRunDefinition: prompt, skill,
tools, copy, ask policy. ProgramRun extends it in
src/lib/programs/program-run.ts with the three session-taking completion
hooks (postRun, buildOutroData, buildOutroNextSteps) that only the
legacy adapter calls. Programs import ProgramRun from there; the agent
no longer references WizardSession anywhere.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
Both read and write the session, drive the OAuth flow through the UI and
take a ProgramId. Plan section 4.5: programs own authentication and
refresh. File and test move unchanged; five importers repoint.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
… programs

yara-hooks and the audit ledger tools imported filename constants and
the AuditCheck shape from three programs. The document names now live
in @lib/constants, the ledger contract (file, check shape, read-side
coercion) in the new leaf @lib/audit-ledger, and each program re-exports
its own, so the audit views and the ledger watcher are unchanged.
Nothing agent-side imports @lib/programs at runtime any more.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
…data

PackageManagerDetector, PackageManagerInfo, DetectedPackageManager and
detectNodePackageManagers move into @utils/package-manager, which already
owns the lockfile detection they wrap. src/lib/detection/package-manager
re-exports them, so every framework config is unchanged; the agent's two
harnesses and the tools server now import the detector contract without
importing detection.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
createBenchmarkPipeline takes the run's emitter; the summary and JSON
writer plugins receive it through MiddlewareFactoryOptions and emit one
`log` event per line they used to print through getUI(). The legacy
adapter's reducer maps each back to WizardUI.log.info, so --benchmark
output is unchanged. Nothing under src/lib/middleware imports src/ui.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
flushScanReport takes `{ yaraReport }` and returns the report line when
it wrote one. The runner emits it as a `log` progress event from its
single flush seam; the legacy adapter's cleanup prints it through the UI
as before. yara-hooks no longer imports the UI or the session type.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
publish_handoff emits `{ kind: 'handoff', text }` instead of calling
getUI().setHandoffText. Both facades (the MCP server and the pi tools)
receive the run's emitter, the snapshot keeps the text as
RunSnapshot.handoffText, and the legacy adapter's reducer maps the event
to WizardUI.setHandoffText so the TUI and the headless host see exactly
what they saw before.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
ESLint no-restricted-imports on today's agent paths: no @ui, session,
detection, registry, runners, commands, steps, frameworks, setup-utils
or oauth imports of any kind, no wizardAbort, and @lib/programs as
types only until B1 moves PROGRAM_BINDINGS. No file allowlist; the rule
runs in the editor and in `pnpm lint`. A2b collapses the path list to
src/agent/**.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
@github-actions

Copy link
Copy Markdown

🧙 Wizard CI

Run the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands:

Test all apps:

  • /wizard-ci all

Test all apps in a directory:

  • /wizard-ci ai-observability
  • /wizard-ci basic-integration
  • /wizard-ci mcp-analytics
  • /wizard-ci replay-vision
  • /wizard-ci revenue
  • /wizard-ci self-driving
  • /wizard-ci warehouse
  • /wizard-ci warehouse-seeded

Test an individual app:

  • /wizard-ci ai-observability/anthropic
  • /wizard-ci ai-observability/google-adk
  • /wizard-ci ai-observability/groq
Show more apps
  • /wizard-ci ai-observability/manual-capture
  • /wizard-ci ai-observability/openai
  • /wizard-ci ai-observability/openai-agents
  • /wizard-ci ai-observability/opentelemetry
  • /wizard-ci ai-observability/vercel-ai
  • /wizard-ci basic-integration/android
  • /wizard-ci basic-integration/angular
  • /wizard-ci basic-integration/astro
  • /wizard-ci basic-integration/django
  • /wizard-ci basic-integration/fastapi
  • /wizard-ci basic-integration/flask
  • /wizard-ci basic-integration/flutter
  • /wizard-ci basic-integration/javascript-node
  • /wizard-ci basic-integration/javascript-web
  • /wizard-ci basic-integration/laravel
  • /wizard-ci basic-integration/next-js
  • /wizard-ci basic-integration/nuxt
  • /wizard-ci basic-integration/python
  • /wizard-ci basic-integration/rails
  • /wizard-ci basic-integration/react-native
  • /wizard-ci basic-integration/react-router
  • /wizard-ci basic-integration/sveltekit
  • /wizard-ci basic-integration/swift
  • /wizard-ci basic-integration/tanstack-router
  • /wizard-ci basic-integration/tanstack-start
  • /wizard-ci basic-integration/vue
  • /wizard-ci mcp-analytics/custom-dispatcher
  • /wizard-ci mcp-analytics/typescript-sdk
  • /wizard-ci replay-vision/javascript-node
  • /wizard-ci replay-vision/next-js
  • /wizard-ci replay-vision/react-vite
  • /wizard-ci revenue/stripe
  • /wizard-ci self-driving/astro
  • /wizard-ci self-driving/fastapi
  • /wizard-ci self-driving/nuxt
  • /wizard-ci self-driving/react-router
  • /wizard-ci self-driving/sveltekit
  • /wizard-ci warehouse/monorepo-env
  • /wizard-ci warehouse/multi-source-next
  • /wizard-ci warehouse/stripe-node
  • /wizard-ci warehouse/zero-source
  • /wizard-ci warehouse-seeded/next-stripe
  • /wizard-ci warehouse-seeded/next-stripe-declined

Test against a Context Mill branch:

  • /wizard-ci all context-mill:my-branch

Add context-mill:<branch> to any command above to pin the Context Mill branch. It defaults to main.

Results will be posted here when complete.

Comment thread .eslintrc.cjs

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will thrash a lot, so consider this a transitional state

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is: A2b narrows this fence to src/agent/**, and C2d deletes it for per-layer TypeScript configs, where the agent's paths map only @env, @shared and @utils:

"paths": {
"@env": [
"../../.tsbuild/env/env.d.ts"
],
"@shared/*": [
"../../.tsbuild/shared/*"
],
"@utils/*": [
"../../.tsbuild/shared/utils/*"
],

Comment thread src/lib/yara-hooks.ts
// programs declare their own doc paths, the hooks read from a generic
// registry. For now: every new program emitting a PII-shaped report has
// to be added here. Land that cleanup before adding a fourth entry.
// TODO(wizard#594): invert this dependency. The document names are shared

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LMAO ancient TODO

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It stays for now, tagged with #594, the open issue for inverting it:

// TODO(wizard#594): invert this dependency. The document names are shared
// constants now, so nothing here imports a program, but this file still knows
// which programs write PII-shaped reports: every new one has to be added.
// Proper fix is inverted — programs declare their own doc paths and the hooks
// read a generic registry. Land that before adding a fourth entry.

@gewenyu99 gewenyu99 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NotVincent — automated review. Not written or checked by a person. Verify before acting on any of it.

Comment thread .eslintrc.cjs
'**/ui/**',
'@lib/wizard-session',
'**/wizard-session',
'@lib/detection/**',

@gewenyu99 gewenyu99 Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the potential issue: The import rule is meant to keep the agent separate from the UI and other layers, but directory imports can bypass it.

agent imports the UI directory itself -> rule only matches files inside that directory -> forbidden UI dependency passes lint

Suggested fix: Cover directory imports as well as files inside them, for both aliases and relative imports.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: the fence now also matches the directories themselves and relative steps paths, so ../../ui and @lib/detection fail lint:

wizard/.eslintrc.cjs

Lines 65 to 88 in 3c7dfb5

group: [
'@ui',
'@ui/**',
'**/ui',
'**/ui/**',
'@lib/wizard-session',
'**/wizard-session',
'@lib/detection',
'@lib/detection/**',
'**/detection',
'**/detection/**',
'@lib/registry',
'**/lib/registry',
'@lib/runners',
'@lib/runners/**',
'**/runners',
'**/runners/**',
'**/commands/**',
'@steps',
'@steps/**',
'**/steps',
'**/steps/**',
'@frameworks/**',
'**/frameworks/**',

Comment thread .eslintrc.cjs
],
excludedFiles: ['**/__tests__/**'],
rules: {
'@typescript-eslint/no-restricted-imports': [

@gewenyu99 gewenyu99 Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the potential issue: The import rule blocks static UI imports but allows the same dependency through a dynamic import.

agent loads the UI with a dynamic import -> rule doesn't inspect it -> forbidden UI dependency passes lint

Suggested fix: Apply the same dependency restrictions to static and dynamic imports.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left for C2d: ESLint's import rule can't see import(), and the per-layer TypeScript configs that replace this fence reject dynamic imports too; the fence comment now says only direct static imports are checked (https://github.com/PostHog/wizard/blob/3c7dfb5a/.eslintrc.cjs).

Comment thread .eslintrc.cjs Outdated
{
// The agent surface. It takes resolved data in, reports through
// progress events and asks through an injected answerer, so nothing
// here may reach a UI, the session, detection, the CLI or a program at

@gewenyu99 gewenyu99 Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the potential issue: This comment promises the agent won't load the UI at runtime, but allowed helpers still bring it in indirectly.

agent imports the debug helper -> helper imports the UI -> agent can load the UI while lint passes

Suggested fix: Describe this as a direct import restriction until the indirect UI dependencies are removed.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworded: the comment now says only direct static imports are checked:

wizard/.eslintrc.cjs

Lines 34 to 39 in 3c7dfb5

// The agent surface. It takes resolved data in, reports through
// progress events and asks through an injected answerer, so nothing
// here may import a UI, the session, detection, the CLI or a program.
// Only direct static imports are checked. Program types stay importable
// until B1 moves PROGRAM_BINDINGS to programs. Today's paths; A2b
// collapses them to src/agent/**.

}),
execute(_id, args) {
const result = publishHandoff(args.content);
const result = publishHandoff(args.content, ctx.emit);

@gewenyu99 gewenyu99 Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the potential issue: A later edit could stop handoff text reaching the host while the tool still reports success, and the current tests wouldn't catch it.

handoff tool stops passing its progress callback -> report file is written but host receives no handoff text -> focused tests still pass

Suggested fix: Exercise the registered Pi and MCP handoff tools and check that their report text reaches the host.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a test that calls both registered publish_handoff tools, Pi and MCP, and checks that the report reaches the host's emit:

it('the Pi tool reports the handoff to the host', async () => {
const tools = createWizardPiTools({
workingDirectory,
skillsBaseUrl: 'http://localhost:0',
emit: (event) => events.push(event),
});
const tool = tools.find((t) => t.name === PUBLISH_HANDOFF_TOOL_NAME);
if (!tool) throw new Error('publish_handoff not registered');
const result = (await (
tool.execute as (id: string, args: unknown) => Promise<unknown>
)('call-1', { content: REPORT })) as { content: [{ text: string }] };
expect(result.content[0].text).toContain('Handoff published');
expect(handoffs()).toEqual([REPORT]);
});
it('the MCP tool reports the handoff to the host', async () => {
const server = (await createWizardToolsServer({
workingDirectory,
detectPackageManager: vi.fn(),
skillsBaseUrl: 'http://localhost:0',
triageProvider: {} as LLMProvider,
emit: (event) => events.push(event),
})) as unknown as {
tools: { name: string; handler: (args: unknown) => unknown }[];
};
const tool = server.tools.find((t) => t.name === PUBLISH_HANDOFF_TOOL_NAME);
if (!tool) throw new Error('publish_handoff not registered');
const result = (await tool.handler({ content: REPORT })) as {
content: [{ text: string }];
isError?: boolean;
};
expect(result.isError).toBeUndefined();
expect(result.content[0].text).toContain('Handoff published');
expect(handoffs()).toEqual([REPORT]);
});


const middleware = input.flags.benchmark
? createBenchmarkPipeline(spinner, runOptions(input))
? createBenchmarkPipeline(emit, spinner, runOptions(input))

@gewenyu99 gewenyu99 Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the potential issue: Benchmark reporting is connected through the runner, but the tests supply their own callback. They won't catch that connection being lost.

benchmark mode is on -> runner passes a callback that does nothing -> benchmark output disappears while middleware tests still pass

Suggested fix: Run a benchmark-enabled sequence in a test and check that its progress observer receives the benchmark output.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a test that runs a linear sequence with --benchmark and checks that the benchmark lines reach onProgress:

it('sends benchmark output to onProgress when benchmarking', async () => {
const benchmarkPath = path.join(tmp, 'benchmark.json');
const configPath = path.join(tmp, '.benchmark-config.json');
fs.writeFileSync(
configPath,
JSON.stringify({ output: { benchmarkPath, logEnabled: false } }),
);
vi.stubEnv('POSTHOG_WIZARD_BENCHMARK_CONFIG', configPath);
vi.stubEnv('POSTHOG_WIZARD_BENCHMARK_FILE', benchmarkPath);
vi.stubEnv('POSTHOG_WIZARD_LOG_DIR', tmp);
const runInput = input();
runInput.flags.benchmark = true;
const events: AgentProgress[] = [];
try {
const result = await runAgent(config(), runInput, {
onProgress: (e) => events.push(e),
});
expect(result.outcome).toBe('success');
const logs = events.flatMap((e) => (e.kind === 'log' ? [e.message] : []));
expect(logs).toContainEqual(
expect.stringContaining(
`Benchmark data will be written to: ${benchmarkPath}`,
),
);
expect(logs).toContainEqual(
expect.stringContaining(`Results written to ${benchmarkPath}`),
);
expect(fs.existsSync(benchmarkPath)).toBe(true);
} finally {
vi.unstubAllEnvs();
}
});

} finally {
flushScanReport({ yaraReport: input.flags.yaraReport });
const report = flushScanReport({ yaraReport: input.flags.yaraReport });
if (report) log(report);

@gewenyu99 gewenyu99 Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the potential issue: The scan tests never produce a summary, so they won't catch it disappearing from the terminal while the report file still exists.

scan finishes or run is aborted -> generated summary is discarded -> report file remains but terminal summary disappears, and focused tests still pass

Suggested fix: Return a real summary in completion and abort tests, and check that each path sends it to terminal output.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests now return a real summary and check that it reaches onProgress on completion, agent abort, crash and both host cancels, and #1303 fixes the cancel-before-start path that dropped it:

it.each<
[string, (host: AbortController) => Partial<RunAgentOptions>, string]
>([
['completes', () => ({}), 'success'],
[
'stops itself',
() => {
harnessState.result = {
kind: 'abort',
classification: AgentErrorType.ABORT,
message: 'No Stripe found',
};
return {};
},
'failed',
],
[
'crashes',
() => {
harnessState.throws = new Error('SDK exploded');
return {};
},
'crashed',
],
[
'is cancelled mid-run',
(host) => {
harnessState.askQuestions = [
{ id: 'q1', prompt: 'Continue?', kind: 'text' },
];
return {
interaction: {
ask: () => {
host.abort();
return new Promise<AskAnswers>(() => undefined);
},
},
};
},
'aborted',
],
[
'is cancelled before it starts',
(host) => {
host.abort();
return {};
},
'aborted',
],
])(
'sends the scan summary to onProgress when the run %s',
async (_ending, arrange, outcome) => {
const summary =
'YARA scan report: /tmp/yara.json\n— YARA Scanner Summary —';
vi.mocked(flushScanReport).mockReturnValueOnce(summary);
const host = new AbortController();
const options = arrange(host);
const runInput = input();
runInput.flags.yaraReport = true;
const events: AgentProgress[] = [];
const result = await runAgent(config(), runInput, {
...options,
signal: host.signal,
onProgress: (e) => events.push(e),
});
expect(result.outcome).toBe(outcome);
expect(flushScanReport).toHaveBeenCalledWith({ yaraReport: true });
expect(events).toContainEqual({
kind: 'log',
level: 'info',
message: summary,
});
},
);

@gewenyu99
gewenyu99 added this pull request to stack #1304 September 22, 2026 19:57
Carries main's eight fixes and A1's per-request ask signal and host-owned
analytics shutdown. One conflict, in the project-scope test's imports:
main dropped AGENTIC_DETECTION_TIMEOUT_MS with its detection retry, and A2a
moved authenticate to programs.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
Carries the guard that keeps a successful run successful when its
terminal analytics flush fails. One import conflict in the adapter test:
authenticate lives in programs here.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
The agent fence matched files inside ui, detection, runners and steps but
not the directories themselves, so `../../ui` or `@lib/detection` passed
lint. Relative steps imports were not covered at all. A probe with eight
such imports gave 1 error before and 8 after. The fence comment now says
that only direct static imports are checked; shared helpers still reach
the UI transitively.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
Each of these reaches the host through a callback the runner or a tool
facade passes on, and the existing tests supplied their own callback. The
new tests go through the real wiring: both registered publish_handoff
tools (Pi and MCP), a linear run with --benchmark, and a scan summary on
completion, agent abort and crash. Dropping any of those callbacks now
fails a test; each was checked by removing it.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
Brings in main (#1235, 2.77.0) through A1. One conflict: the adapter's runner
import keeps A2a's removal of ProgramRun and takes A1's TASK_OUTCOMES_KEY.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
Brings in main (#1319). No conflicts.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
@gewenyu99
gewenyu99 marked this pull request as ready for review September 23, 2026 21:15
@gewenyu99
gewenyu99 requested review from a team as code owners September 23, 2026 21:15
@gewenyu99
gewenyu99 merged commit 9d68235 into main Sep 23, 2026
29 checks passed
@gewenyu99
gewenyu99 deleted the posthog/functional-a2a-edges branch September 23, 2026 21:21
gewenyu99 added a commit that referenced this pull request Sep 24, 2026
Release A landed on main as squash commits (#1293, #1297, #1299, #1303).
B1 already carries that content through the A3 branch, so the merge
keeps B1's tree and adds #1334, the one change main has beyond A3, with
B1 import paths.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
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