Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2a6caaf
feat: wire telemetry withCommandRun into all add.* commands
Hweinstock Apr 29, 2026
a0eb4ec
refactor: extract cliCommandRun helper, apply to all add.* primitives
Hweinstock Apr 29, 2026
b102b94
test: add audit file assertions for all add.* telemetry
Hweinstock Apr 29, 2026
72ab9d3
test: add telemetry audit assertions to existing add integ tests
Hweinstock Apr 29, 2026
e521dbc
refactor: extract shared audit test utils into src/test-utils/audit.ts
Hweinstock Apr 29, 2026
f152b0c
fix: address review feedback — guard telemetry init, replaceAll, unkn…
Hweinstock Apr 29, 2026
f7003ca
fix: AgentPrimitive TUI try/catch, standardize uses safeParse
Hweinstock Apr 29, 2026
091264c
refactor: extract standalone assertTelemetry helper
Hweinstock Apr 30, 2026
8aecebf
refactor: rename audit.ts to telemetry-helper.ts, clarify method names
Hweinstock Apr 30, 2026
8e4a229
refactor: move assertTelemetry into TelemetryHelper as assertMetricEm…
Hweinstock Apr 30, 2026
445a448
feat: add telemetry to TUI add paths via withAddTelemetry
Hweinstock Apr 30, 2026
c0af1ea
fix: review feedback — withAddTelemetry safety, standardize handles u…
Hweinstock Apr 30, 2026
db7300b
fix: remove unnecessary type assertion
Hweinstock Apr 30, 2026
de7c6df
fix: address review — document standardize cast, add policy-engine + …
Hweinstock Apr 30, 2026
9087af0
refactor: centralize gateway target type mapping in common-shapes
Hweinstock Apr 30, 2026
35a0531
fix: preserve original function error with telemetry wrapper
Hweinstock Apr 30, 2026
0d09e63
refactor: extract telemetryAttrs into a single line
Hweinstock Apr 30, 2026
3d948c4
feat: wire up telemetry for addAgent
Hweinstock Apr 30, 2026
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
2 changes: 1 addition & 1 deletion e2e-tests/byo-custom-jwt.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,7 +48,7 @@ const region = process.env.AWS_REGION ?? 'us-east-1';
* Run the local CLI build without skipping install (needed for deploy).
*/
function runLocalCLI(args: string[], cwd: string): Promise<RunResult> {
return runCLI(args, cwd, /* skipInstall */ false);
return runCLI(args, cwd, { skipInstall: false });
}

describe.sequential('e2e: BYO agent with CUSTOM_JWT auth', () => {
Expand Down
58 changes: 55 additions & 3 deletions integ-tests/add-remove-resources.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
import { createTestProject, readProjectConfig, runCLI } from '../src/test-utils/index.js';
import type { TestProject } from '../src/test-utils/index.js';
import { createTelemetryHelper } from '../src/test-utils/telemetry-helper.js';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

const telemetry = createTelemetryHelper();

describe('integration: add and remove resources', () => {
let project: TestProject;

Expand All@@ -16,13 +19,16 @@ describe('integration: add and remove resources', () => {

afterAll(async () => {
await project.cleanup();
telemetry.destroy();
});

describe('memory lifecycle', () => {
const memoryName = `IntegMem${Date.now().toString().slice(-6)}`;

it('adds a memory resource', async () => {
const result = await runCLI(['add', 'memory', '--name', memoryName, '--json'], project.projectPath);
const result = await runCLI(['add', 'memory', '--name', memoryName, '--json'], project.projectPath, {
env: telemetry.env,
});

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
Expand All@@ -34,13 +40,17 @@ describe('integration: add and remove resources', () => {
expect(memories, 'memories should exist').toBeDefined();
const found = memories!.some((m: Record<string, unknown>) => m.name === memoryName);
expect(found, `Memory "${memoryName}" should be in config`).toBe(true);

// Verify telemetry
telemetry.assertMetricEmitted({ command: 'add.memory', exit_reason: 'success' });
});

it('adds a memory with EPISODIC strategy and verifies reflectionNamespaces', async () => {
const episodicMemName = `EpiMem${Date.now().toString().slice(-6)}`;
const result = await runCLI(
['add', 'memory', '--name', episodicMemName, '--strategies', 'EPISODIC', '--json'],
project.projectPath
project.projectPath,
{ env: telemetry.env }
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
Expand All@@ -61,6 +71,14 @@ describe('integration: add and remove resources', () => {
expect(episodic!.reflectionNamespaces, 'Should have reflectionNamespaces').toBeDefined();
expect(episodic!.reflectionNamespaces!.length).toBeGreaterThan(0);

// Verify telemetry
telemetry.assertMetricEmitted({
command: 'add.memory',
exit_reason: 'success',
strategy_count: '1',
strategy_episodic: 'true',
});

// Clean up
await runCLI(['remove', 'memory', '--name', episodicMemName, '--json'], project.projectPath);
});
Expand All@@ -86,7 +104,8 @@ describe('integration: add and remove resources', () => {
it('adds a credential resource', async () => {
const result = await runCLI(
['add', 'credential', '--name', credentialName, '--api-key', 'test-key-integ-123', '--json'],
project.projectPath
project.projectPath,
{ env: telemetry.env }
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
Expand All@@ -99,6 +118,13 @@ describe('integration: add and remove resources', () => {
expect(credentials, 'credentials should exist').toBeDefined();
const found = credentials!.some((c: Record<string, unknown>) => c.name === credentialName);
expect(found, `Credential "${credentialName}" should be in config`).toBe(true);

// Verify telemetry
telemetry.assertMetricEmitted({
command: 'add.credential',
exit_reason: 'success',
credential_type: 'api-key',
});
});

it('removes the credential resource', async () => {
Expand All@@ -115,4 +141,30 @@ describe('integration: add and remove resources', () => {
expect(found, `Credential "${credentialName}" should be removed from config`).toBe(false);
});
});

describe('policy-engine', () => {
const engineName = `TestEngine${Date.now().toString().slice(-6)}`;

it('adds a policy engine resource', async () => {
const result = await runCLI(['add', 'policy-engine', '--name', engineName, '--json'], project.projectPath, {
env: telemetry.env,
});

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);

telemetry.assertMetricEmitted({
command: 'add.policy-engine',
exit_reason: 'success',
attach_gateway_count: '0',
});
});

it('removes the policy engine resource', async () => {
const result = await runCLI(['remove', 'policy-engine', '--name', engineName, '--json'], project.projectPath);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
});
});
});
2 changes: 1 addition & 1 deletion integ-tests/create-no-agent.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,7 @@ describe('integration: create without agent', () => {

it.skipIf(!hasNpm || !hasGit)('creates project with real npm install and git init', async () => {
const name = `NoAgent${Date.now().toString().slice(-6)}`;
const result = await runCLI(['create', '--name', name, '--no-agent', '--json'], testDir, false);
const result = await runCLI(['create', '--name', name, '--no-agent', '--json'], testDir, { skipInstall: false });

expect(result.exitCode, `stderr: ${result.stderr}`).toBe(0);

Expand Down
2 changes: 1 addition & 1 deletion integ-tests/create-with-agent.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,7 +49,7 @@ describe('integration: create with Python agent', () => {
'--json',
],
testDir,
false
{ skipInstall: false }
);

expect(result.exitCode, `stderr: ${result.stderr}`).toBe(0);
Expand Down
2 changes: 1 addition & 1 deletion integ-tests/dev-server.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,7 +60,7 @@ describe('integration: dev server', () => {
'--json',
],
testDir,
false
{ skipInstall: false }
);

if (result.exitCode === 0) {
Expand Down
47 changes: 20 additions & 27 deletions integ-tests/help.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
import { spawnAndCollect } from '../src/test-utils/cli-runner.js';
import { runCLI } from '../src/test-utils/index.js';
import { createTelemetryHelper } from '../src/test-utils/telemetry-helper.js';
import { readdirSync } from 'node:fs';
import { mkdir, readFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { afterAll, describe, expect, it } from 'vitest';

const COMMANDS = [
'create',
Expand DownExpand Up@@ -45,52 +44,46 @@ describe('CLI help', () => {
});

describe('help modes telemetry', () => {
let testConfigDir: string;
const telemetry = createTelemetryHelper();
const cliPath = join(__dirname, '..', 'dist', 'cli', 'index.mjs');

beforeAll(async () => {
testConfigDir = join(tmpdir(), `agentcore-help-telemetry-${Date.now()}`);
await mkdir(testConfigDir, { recursive: true });
});
afterAll(() => rm(testConfigDir, { recursive: true, force: true }));
afterAll(() => telemetry.destroy());

function run(args: string[], extraEnv: Record<string, string> = {}) {
return spawnAndCollect('node', [cliPath, ...args], tmpdir(), {
return spawnAndCollect('node', [cliPath, ...args], process.cwd(), {
AGENTCORE_SKIP_INSTALL: '1',
AGENTCORE_CONFIG_DIR: testConfigDir,
...telemetry.env,
...extraEnv,
});
}

it('writes JSONL audit file when audit is enabled via env var', async () => {
const result = await run(['help', 'modes'], { AGENTCORE_TELEMETRY_AUDIT: '1' });
const result = await run(['help', 'modes']);
expect(result.exitCode).toBe(0);

const telemetryDir = join(testConfigDir, 'telemetry');
const files = readdirSync(telemetryDir).filter(f => f.startsWith('help-'));
expect(files).toHaveLength(1);

const content = await readFile(join(telemetryDir, files[0]!), 'utf-8');
const entry = JSON.parse(content.trim());
expect(entry.attrs).toMatchObject({
'service.name': 'agentcore-cli',
'agentcore-cli.mode': 'cli',
const entries = telemetry.readEntries();
expect(entries).toHaveLength(1);
telemetry.assertMetricEmitted({
command_group: 'help',
command: 'help.modes',
exit_reason: 'success',
});
expect(entry.attrs['agentcore-cli.session_id']).toBeDefined();
expect(entry.attrs['os.type']).toBeDefined();
expect(entry.value).toBeGreaterThanOrEqual(0);
expect(entries[0]!.attrs['agentcore-cli.session_id']).toBeDefined();
expect(entries[0]!.attrs['os.type']).toBeDefined();
expect(entries[0]!.value).toBeGreaterThanOrEqual(0);
});

it('does not write audit file when audit is not enabled', async () => {
const telemetryDir = join(testConfigDir, 'telemetry');
await rm(telemetryDir, { recursive: true, force: true });
telemetry.clearEntries();

const result = await run(['help', 'modes']);
const noAuditCliPath = join(__dirname, '..', 'dist', 'cli', 'index.mjs');
const result = await spawnAndCollect('node', [noAuditCliPath, 'help', 'modes'], process.cwd(), {
AGENTCORE_SKIP_INSTALL: '1',
AGENTCORE_CONFIG_DIR: telemetry.dir,
});
expect(result.exitCode).toBe(0);

const telemetryDir = join(telemetry.dir, 'telemetry');
try {
const files = readdirSync(telemetryDir);
expect(files).toHaveLength(0);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2a6caaf
feat: wire telemetry withCommandRun into all add.* commands
Hweinstock Apr 29, 2026
a0eb4ec
refactor: extract cliCommandRun helper, apply to all add.* primitives
Hweinstock Apr 29, 2026
b102b94
test: add audit file assertions for all add.* telemetry
Hweinstock Apr 29, 2026
72ab9d3
test: add telemetry audit assertions to existing add integ tests
Hweinstock Apr 29, 2026
e521dbc
refactor: extract shared audit test utils into src/test-utils/audit.ts
Hweinstock Apr 29, 2026
f152b0c
fix: address review feedback — guard telemetry init, replaceAll, unkn…
Hweinstock Apr 29, 2026
f7003ca
fix: AgentPrimitive TUI try/catch, standardize uses safeParse
Hweinstock Apr 29, 2026
091264c
refactor: extract standalone assertTelemetry helper
Hweinstock Apr 30, 2026
8aecebf
refactor: rename audit.ts to telemetry-helper.ts, clarify method names
Hweinstock Apr 30, 2026
8e4a229
refactor: move assertTelemetry into TelemetryHelper as assertMetricEm…
Hweinstock Apr 30, 2026
445a448
feat: add telemetry to TUI add paths via withAddTelemetry
Hweinstock Apr 30, 2026
c0af1ea
fix: review feedback — withAddTelemetry safety, standardize handles u…
Hweinstock Apr 30, 2026
db7300b
fix: remove unnecessary type assertion
Hweinstock Apr 30, 2026
de7c6df
fix: address review — document standardize cast, add policy-engine + …
Hweinstock Apr 30, 2026
9087af0
refactor: centralize gateway target type mapping in common-shapes
Hweinstock Apr 30, 2026
35a0531
fix: preserve original function error with telemetry wrapper
Hweinstock Apr 30, 2026
0d09e63
refactor: extract telemetryAttrs into a single line
Hweinstock Apr 30, 2026
3d948c4
feat: wire up telemetry for addAgent
Hweinstock Apr 30, 2026
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
2 changes: 1 addition & 1 deletion e2e-tests/byo-custom-jwt.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,7 +48,7 @@ const region = process.env.AWS_REGION ?? 'us-east-1';
* Run the local CLI build without skipping install (needed for deploy).
*/
function runLocalCLI(args: string[], cwd: string): Promise<RunResult> {
return runCLI(args, cwd, /* skipInstall */ false);
return runCLI(args, cwd, { skipInstall: false });
}

describe.sequential('e2e: BYO agent with CUSTOM_JWT auth', () => {
Expand Down
58 changes: 55 additions & 3 deletions integ-tests/add-remove-resources.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
import { createTestProject, readProjectConfig, runCLI } from '../src/test-utils/index.js';
import type { TestProject } from '../src/test-utils/index.js';
import { createTelemetryHelper } from '../src/test-utils/telemetry-helper.js';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

const telemetry = createTelemetryHelper();

describe('integration: add and remove resources', () => {
let project: TestProject;

Expand All@@ -16,13 +19,16 @@ describe('integration: add and remove resources', () => {

afterAll(async () => {
await project.cleanup();
telemetry.destroy();
});

describe('memory lifecycle', () => {
const memoryName = `IntegMem${Date.now().toString().slice(-6)}`;

it('adds a memory resource', async () => {
const result = await runCLI(['add', 'memory', '--name', memoryName, '--json'], project.projectPath);
const result = await runCLI(['add', 'memory', '--name', memoryName, '--json'], project.projectPath, {
env: telemetry.env,
});

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
Expand All@@ -34,13 +40,17 @@ describe('integration: add and remove resources', () => {
expect(memories, 'memories should exist').toBeDefined();
const found = memories!.some((m: Record<string, unknown>) => m.name === memoryName);
expect(found, `Memory "${memoryName}" should be in config`).toBe(true);

// Verify telemetry
telemetry.assertMetricEmitted({ command: 'add.memory', exit_reason: 'success' });
});

it('adds a memory with EPISODIC strategy and verifies reflectionNamespaces', async () => {
const episodicMemName = `EpiMem${Date.now().toString().slice(-6)}`;
const result = await runCLI(
['add', 'memory', '--name', episodicMemName, '--strategies', 'EPISODIC', '--json'],
project.projectPath
project.projectPath,
{ env: telemetry.env }
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
Expand All@@ -61,6 +71,14 @@ describe('integration: add and remove resources', () => {
expect(episodic!.reflectionNamespaces, 'Should have reflectionNamespaces').toBeDefined();
expect(episodic!.reflectionNamespaces!.length).toBeGreaterThan(0);

// Verify telemetry
telemetry.assertMetricEmitted({
command: 'add.memory',
exit_reason: 'success',
strategy_count: '1',
strategy_episodic: 'true',
});

// Clean up
await runCLI(['remove', 'memory', '--name', episodicMemName, '--json'], project.projectPath);
});
Expand All@@ -86,7 +104,8 @@ describe('integration: add and remove resources', () => {
it('adds a credential resource', async () => {
const result = await runCLI(
['add', 'credential', '--name', credentialName, '--api-key', 'test-key-integ-123', '--json'],
project.projectPath
project.projectPath,
{ env: telemetry.env }
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
Expand All@@ -99,6 +118,13 @@ describe('integration: add and remove resources', () => {
expect(credentials, 'credentials should exist').toBeDefined();
const found = credentials!.some((c: Record<string, unknown>) => c.name === credentialName);
expect(found, `Credential "${credentialName}" should be in config`).toBe(true);

// Verify telemetry
telemetry.assertMetricEmitted({
command: 'add.credential',
exit_reason: 'success',
credential_type: 'api-key',
});
});

it('removes the credential resource', async () => {
Expand All@@ -115,4 +141,30 @@ describe('integration: add and remove resources', () => {
expect(found, `Credential "${credentialName}" should be removed from config`).toBe(false);
});
});

describe('policy-engine', () => {
const engineName = `TestEngine${Date.now().toString().slice(-6)}`;

it('adds a policy engine resource', async () => {
const result = await runCLI(['add', 'policy-engine', '--name', engineName, '--json'], project.projectPath, {
env: telemetry.env,
});

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);

telemetry.assertMetricEmitted({
command: 'add.policy-engine',
exit_reason: 'success',
attach_gateway_count: '0',
});
});

it('removes the policy engine resource', async () => {
const result = await runCLI(['remove', 'policy-engine', '--name', engineName, '--json'], project.projectPath);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
});
});
});
2 changes: 1 addition & 1 deletion integ-tests/create-no-agent.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,7 @@ describe('integration: create without agent', () => {

it.skipIf(!hasNpm || !hasGit)('creates project with real npm install and git init', async () => {
const name = `NoAgent${Date.now().toString().slice(-6)}`;
const result = await runCLI(['create', '--name', name, '--no-agent', '--json'], testDir, false);
const result = await runCLI(['create', '--name', name, '--no-agent', '--json'], testDir, { skipInstall: false });

expect(result.exitCode, `stderr: ${result.stderr}`).toBe(0);

Expand Down
2 changes: 1 addition & 1 deletion integ-tests/create-with-agent.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,7 +49,7 @@ describe('integration: create with Python agent', () => {
'--json',
],
testDir,
false
{ skipInstall: false }
);

expect(result.exitCode, `stderr: ${result.stderr}`).toBe(0);
Expand Down
2 changes: 1 addition & 1 deletion integ-tests/dev-server.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,7 +60,7 @@ describe('integration: dev server', () => {
'--json',
],
testDir,
false
{ skipInstall: false }
);

if (result.exitCode === 0) {
Expand Down
47 changes: 20 additions & 27 deletions integ-tests/help.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
import { spawnAndCollect } from '../src/test-utils/cli-runner.js';
import { runCLI } from '../src/test-utils/index.js';
import { createTelemetryHelper } from '../src/test-utils/telemetry-helper.js';
import { readdirSync } from 'node:fs';
import { mkdir, readFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { afterAll, describe, expect, it } from 'vitest';

const COMMANDS = [
'create',
Expand DownExpand Up@@ -45,52 +44,46 @@ describe('CLI help', () => {
});

describe('help modes telemetry', () => {
let testConfigDir: string;
const telemetry = createTelemetryHelper();
const cliPath = join(__dirname, '..', 'dist', 'cli', 'index.mjs');

beforeAll(async () => {
testConfigDir = join(tmpdir(), `agentcore-help-telemetry-${Date.now()}`);
await mkdir(testConfigDir, { recursive: true });
});
afterAll(() => rm(testConfigDir, { recursive: true, force: true }));
afterAll(() => telemetry.destroy());

function run(args: string[], extraEnv: Record<string, string> = {}) {
return spawnAndCollect('node', [cliPath, ...args], tmpdir(), {
return spawnAndCollect('node', [cliPath, ...args], process.cwd(), {
AGENTCORE_SKIP_INSTALL: '1',
AGENTCORE_CONFIG_DIR: testConfigDir,
...telemetry.env,
...extraEnv,
});
}

it('writes JSONL audit file when audit is enabled via env var', async () => {
const result = await run(['help', 'modes'], { AGENTCORE_TELEMETRY_AUDIT: '1' });
const result = await run(['help', 'modes']);
expect(result.exitCode).toBe(0);

const telemetryDir = join(testConfigDir, 'telemetry');
const files = readdirSync(telemetryDir).filter(f => f.startsWith('help-'));
expect(files).toHaveLength(1);

const content = await readFile(join(telemetryDir, files[0]!), 'utf-8');
const entry = JSON.parse(content.trim());
expect(entry.attrs).toMatchObject({
'service.name': 'agentcore-cli',
'agentcore-cli.mode': 'cli',
const entries = telemetry.readEntries();
expect(entries).toHaveLength(1);
telemetry.assertMetricEmitted({
command_group: 'help',
command: 'help.modes',
exit_reason: 'success',
});
expect(entry.attrs['agentcore-cli.session_id']).toBeDefined();
expect(entry.attrs['os.type']).toBeDefined();
expect(entry.value).toBeGreaterThanOrEqual(0);
expect(entries[0]!.attrs['agentcore-cli.session_id']).toBeDefined();
expect(entries[0]!.attrs['os.type']).toBeDefined();
expect(entries[0]!.value).toBeGreaterThanOrEqual(0);
});

it('does not write audit file when audit is not enabled', async () => {
const telemetryDir = join(testConfigDir, 'telemetry');
await rm(telemetryDir, { recursive: true, force: true });
telemetry.clearEntries();

const result = await run(['help', 'modes']);
const noAuditCliPath = join(__dirname, '..', 'dist', 'cli', 'index.mjs');
const result = await spawnAndCollect('node', [noAuditCliPath, 'help', 'modes'], process.cwd(), {
AGENTCORE_SKIP_INSTALL: '1',
AGENTCORE_CONFIG_DIR: telemetry.dir,
});
expect(result.exitCode).toBe(0);

const telemetryDir = join(telemetry.dir, 'telemetry');
try {
const files = readdirSync(telemetryDir);
expect(files).toHaveLength(0);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2a6caaf
feat: wire telemetry withCommandRun into all add.* commands
Hweinstock Apr 29, 2026
a0eb4ec
refactor: extract cliCommandRun helper, apply to all add.* primitives
Hweinstock Apr 29, 2026
b102b94
test: add audit file assertions for all add.* telemetry
Hweinstock Apr 29, 2026
72ab9d3
test: add telemetry audit assertions to existing add integ tests
Hweinstock Apr 29, 2026
e521dbc
refactor: extract shared audit test utils into src/test-utils/audit.ts
Hweinstock Apr 29, 2026
f152b0c
fix: address review feedback — guard telemetry init, replaceAll, unkn…
Hweinstock Apr 29, 2026
f7003ca
fix: AgentPrimitive TUI try/catch, standardize uses safeParse
Hweinstock Apr 29, 2026
091264c
refactor: extract standalone assertTelemetry helper
Hweinstock Apr 30, 2026
8aecebf
refactor: rename audit.ts to telemetry-helper.ts, clarify method names
Hweinstock Apr 30, 2026
8e4a229
refactor: move assertTelemetry into TelemetryHelper as assertMetricEm…
Hweinstock Apr 30, 2026
445a448
feat: add telemetry to TUI add paths via withAddTelemetry
Hweinstock Apr 30, 2026
c0af1ea
fix: review feedback — withAddTelemetry safety, standardize handles u…
Hweinstock Apr 30, 2026
db7300b
fix: remove unnecessary type assertion
Hweinstock Apr 30, 2026
de7c6df
fix: address review — document standardize cast, add policy-engine + …
Hweinstock Apr 30, 2026
9087af0
refactor: centralize gateway target type mapping in common-shapes
Hweinstock Apr 30, 2026
35a0531
fix: preserve original function error with telemetry wrapper
Hweinstock Apr 30, 2026
0d09e63
refactor: extract telemetryAttrs into a single line
Hweinstock Apr 30, 2026
3d948c4
feat: wire up telemetry for addAgent
Hweinstock Apr 30, 2026
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
2 changes: 1 addition & 1 deletion e2e-tests/byo-custom-jwt.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,7 +48,7 @@ const region = process.env.AWS_REGION ?? 'us-east-1';
* Run the local CLI build without skipping install (needed for deploy).
*/
function runLocalCLI(args: string[], cwd: string): Promise<RunResult> {
return runCLI(args, cwd, /* skipInstall */ false);
return runCLI(args, cwd, { skipInstall: false });
}

describe.sequential('e2e: BYO agent with CUSTOM_JWT auth', () => {
Expand Down
58 changes: 55 additions & 3 deletions integ-tests/add-remove-resources.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
import { createTestProject, readProjectConfig, runCLI } from '../src/test-utils/index.js';
import type { TestProject } from '../src/test-utils/index.js';
import { createTelemetryHelper } from '../src/test-utils/telemetry-helper.js';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

const telemetry = createTelemetryHelper();

describe('integration: add and remove resources', () => {
let project: TestProject;

Expand All@@ -16,13 +19,16 @@ describe('integration: add and remove resources', () => {

afterAll(async () => {
await project.cleanup();
telemetry.destroy();
});

describe('memory lifecycle', () => {
const memoryName = `IntegMem${Date.now().toString().slice(-6)}`;

it('adds a memory resource', async () => {
const result = await runCLI(['add', 'memory', '--name', memoryName, '--json'], project.projectPath);
const result = await runCLI(['add', 'memory', '--name', memoryName, '--json'], project.projectPath, {
env: telemetry.env,
});

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
Expand All@@ -34,13 +40,17 @@ describe('integration: add and remove resources', () => {
expect(memories, 'memories should exist').toBeDefined();
const found = memories!.some((m: Record<string, unknown>) => m.name === memoryName);
expect(found, `Memory "${memoryName}" should be in config`).toBe(true);

// Verify telemetry
telemetry.assertMetricEmitted({ command: 'add.memory', exit_reason: 'success' });
});

it('adds a memory with EPISODIC strategy and verifies reflectionNamespaces', async () => {
const episodicMemName = `EpiMem${Date.now().toString().slice(-6)}`;
const result = await runCLI(
['add', 'memory', '--name', episodicMemName, '--strategies', 'EPISODIC', '--json'],
project.projectPath
project.projectPath,
{ env: telemetry.env }
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
Expand All@@ -61,6 +71,14 @@ describe('integration: add and remove resources', () => {
expect(episodic!.reflectionNamespaces, 'Should have reflectionNamespaces').toBeDefined();
expect(episodic!.reflectionNamespaces!.length).toBeGreaterThan(0);

// Verify telemetry
telemetry.assertMetricEmitted({
command: 'add.memory',
exit_reason: 'success',
strategy_count: '1',
strategy_episodic: 'true',
});

// Clean up
await runCLI(['remove', 'memory', '--name', episodicMemName, '--json'], project.projectPath);
});
Expand All@@ -86,7 +104,8 @@ describe('integration: add and remove resources', () => {
it('adds a credential resource', async () => {
const result = await runCLI(
['add', 'credential', '--name', credentialName, '--api-key', 'test-key-integ-123', '--json'],
project.projectPath
project.projectPath,
{ env: telemetry.env }
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
Expand All@@ -99,6 +118,13 @@ describe('integration: add and remove resources', () => {
expect(credentials, 'credentials should exist').toBeDefined();
const found = credentials!.some((c: Record<string, unknown>) => c.name === credentialName);
expect(found, `Credential "${credentialName}" should be in config`).toBe(true);

// Verify telemetry
telemetry.assertMetricEmitted({
command: 'add.credential',
exit_reason: 'success',
credential_type: 'api-key',
});
});

it('removes the credential resource', async () => {
Expand All@@ -115,4 +141,30 @@ describe('integration: add and remove resources', () => {
expect(found, `Credential "${credentialName}" should be removed from config`).toBe(false);
});
});

describe('policy-engine', () => {
const engineName = `TestEngine${Date.now().toString().slice(-6)}`;

it('adds a policy engine resource', async () => {
const result = await runCLI(['add', 'policy-engine', '--name', engineName, '--json'], project.projectPath, {
env: telemetry.env,
});

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);

telemetry.assertMetricEmitted({
command: 'add.policy-engine',
exit_reason: 'success',
attach_gateway_count: '0',
});
});

it('removes the policy engine resource', async () => {
const result = await runCLI(['remove', 'policy-engine', '--name', engineName, '--json'], project.projectPath);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
});
});
});
2 changes: 1 addition & 1 deletion integ-tests/create-no-agent.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,7 @@ describe('integration: create without agent', () => {

it.skipIf(!hasNpm || !hasGit)('creates project with real npm install and git init', async () => {
const name = `NoAgent${Date.now().toString().slice(-6)}`;
const result = await runCLI(['create', '--name', name, '--no-agent', '--json'], testDir, false);
const result = await runCLI(['create', '--name', name, '--no-agent', '--json'], testDir, { skipInstall: false });

expect(result.exitCode, `stderr: ${result.stderr}`).toBe(0);

Expand Down
2 changes: 1 addition & 1 deletion integ-tests/create-with-agent.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,7 +49,7 @@ describe('integration: create with Python agent', () => {
'--json',
],
testDir,
false
{ skipInstall: false }
);

expect(result.exitCode, `stderr: ${result.stderr}`).toBe(0);
Expand Down
2 changes: 1 addition & 1 deletion integ-tests/dev-server.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,7 +60,7 @@ describe('integration: dev server', () => {
'--json',
],
testDir,
false
{ skipInstall: false }
);

if (result.exitCode === 0) {
Expand Down
47 changes: 20 additions & 27 deletions integ-tests/help.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
import { spawnAndCollect } from '../src/test-utils/cli-runner.js';
import { runCLI } from '../src/test-utils/index.js';
import { createTelemetryHelper } from '../src/test-utils/telemetry-helper.js';
import { readdirSync } from 'node:fs';
import { mkdir, readFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { afterAll, describe, expect, it } from 'vitest';

const COMMANDS = [
'create',
Expand DownExpand Up@@ -45,52 +44,46 @@ describe('CLI help', () => {
});

describe('help modes telemetry', () => {
let testConfigDir: string;
const telemetry = createTelemetryHelper();
const cliPath = join(__dirname, '..', 'dist', 'cli', 'index.mjs');

beforeAll(async () => {
testConfigDir = join(tmpdir(), `agentcore-help-telemetry-${Date.now()}`);
await mkdir(testConfigDir, { recursive: true });
});
afterAll(() => rm(testConfigDir, { recursive: true, force: true }));
afterAll(() => telemetry.destroy());

function run(args: string[], extraEnv: Record<string, string> = {}) {
return spawnAndCollect('node', [cliPath, ...args], tmpdir(), {
return spawnAndCollect('node', [cliPath, ...args], process.cwd(), {
AGENTCORE_SKIP_INSTALL: '1',
AGENTCORE_CONFIG_DIR: testConfigDir,
...telemetry.env,
...extraEnv,
});
}

it('writes JSONL audit file when audit is enabled via env var', async () => {
const result = await run(['help', 'modes'], { AGENTCORE_TELEMETRY_AUDIT: '1' });
const result = await run(['help', 'modes']);
expect(result.exitCode).toBe(0);

const telemetryDir = join(testConfigDir, 'telemetry');
const files = readdirSync(telemetryDir).filter(f => f.startsWith('help-'));
expect(files).toHaveLength(1);

const content = await readFile(join(telemetryDir, files[0]!), 'utf-8');
const entry = JSON.parse(content.trim());
expect(entry.attrs).toMatchObject({
'service.name': 'agentcore-cli',
'agentcore-cli.mode': 'cli',
const entries = telemetry.readEntries();
expect(entries).toHaveLength(1);
telemetry.assertMetricEmitted({
command_group: 'help',
command: 'help.modes',
exit_reason: 'success',
});
expect(entry.attrs['agentcore-cli.session_id']).toBeDefined();
expect(entry.attrs['os.type']).toBeDefined();
expect(entry.value).toBeGreaterThanOrEqual(0);
expect(entries[0]!.attrs['agentcore-cli.session_id']).toBeDefined();
expect(entries[0]!.attrs['os.type']).toBeDefined();
expect(entries[0]!.value).toBeGreaterThanOrEqual(0);
});

it('does not write audit file when audit is not enabled', async () => {
const telemetryDir = join(testConfigDir, 'telemetry');
await rm(telemetryDir, { recursive: true, force: true });
telemetry.clearEntries();

const result = await run(['help', 'modes']);
const noAuditCliPath = join(__dirname, '..', 'dist', 'cli', 'index.mjs');
const result = await spawnAndCollect('node', [noAuditCliPath, 'help', 'modes'], process.cwd(), {
AGENTCORE_SKIP_INSTALL: '1',
AGENTCORE_CONFIG_DIR: telemetry.dir,
});
expect(result.exitCode).toBe(0);

const telemetryDir = join(telemetry.dir, 'telemetry');
try {
const files = readdirSync(telemetryDir);
expect(files).toHaveLength(0);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2a6caaf
feat: wire telemetry withCommandRun into all add.* commands
Hweinstock Apr 29, 2026
a0eb4ec
refactor: extract cliCommandRun helper, apply to all add.* primitives
Hweinstock Apr 29, 2026
b102b94
test: add audit file assertions for all add.* telemetry
Hweinstock Apr 29, 2026
72ab9d3
test: add telemetry audit assertions to existing add integ tests
Hweinstock Apr 29, 2026
e521dbc
refactor: extract shared audit test utils into src/test-utils/audit.ts
Hweinstock Apr 29, 2026
f152b0c
fix: address review feedback — guard telemetry init, replaceAll, unkn…
Hweinstock Apr 29, 2026
f7003ca
fix: AgentPrimitive TUI try/catch, standardize uses safeParse
Hweinstock Apr 29, 2026
091264c
refactor: extract standalone assertTelemetry helper
Hweinstock Apr 30, 2026
8aecebf
refactor: rename audit.ts to telemetry-helper.ts, clarify method names
Hweinstock Apr 30, 2026
8e4a229
refactor: move assertTelemetry into TelemetryHelper as assertMetricEm…
Hweinstock Apr 30, 2026
445a448
feat: add telemetry to TUI add paths via withAddTelemetry
Hweinstock Apr 30, 2026
c0af1ea
fix: review feedback — withAddTelemetry safety, standardize handles u…
Hweinstock Apr 30, 2026
db7300b
fix: remove unnecessary type assertion
Hweinstock Apr 30, 2026
de7c6df
fix: address review — document standardize cast, add policy-engine + …
Hweinstock Apr 30, 2026
9087af0
refactor: centralize gateway target type mapping in common-shapes
Hweinstock Apr 30, 2026
35a0531
fix: preserve original function error with telemetry wrapper
Hweinstock Apr 30, 2026
0d09e63
refactor: extract telemetryAttrs into a single line
Hweinstock Apr 30, 2026
3d948c4
feat: wire up telemetry for addAgent
Hweinstock Apr 30, 2026
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
2 changes: 1 addition & 1 deletion e2e-tests/byo-custom-jwt.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,7 +48,7 @@ const region = process.env.AWS_REGION ?? 'us-east-1';
* Run the local CLI build without skipping install (needed for deploy).
*/
function runLocalCLI(args: string[], cwd: string): Promise<RunResult> {
return runCLI(args, cwd, /* skipInstall */ false);
return runCLI(args, cwd, { skipInstall: false });
}

describe.sequential('e2e: BYO agent with CUSTOM_JWT auth', () => {
Expand Down
58 changes: 55 additions & 3 deletions integ-tests/add-remove-resources.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
import { createTestProject, readProjectConfig, runCLI } from '../src/test-utils/index.js';
import type { TestProject } from '../src/test-utils/index.js';
import { createTelemetryHelper } from '../src/test-utils/telemetry-helper.js';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

const telemetry = createTelemetryHelper();

describe('integration: add and remove resources', () => {
let project: TestProject;

Expand All@@ -16,13 +19,16 @@ describe('integration: add and remove resources', () => {

afterAll(async () => {
await project.cleanup();
telemetry.destroy();
});

describe('memory lifecycle', () => {
const memoryName = `IntegMem${Date.now().toString().slice(-6)}`;

it('adds a memory resource', async () => {
const result = await runCLI(['add', 'memory', '--name', memoryName, '--json'], project.projectPath);
const result = await runCLI(['add', 'memory', '--name', memoryName, '--json'], project.projectPath, {
env: telemetry.env,
});

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
Expand All@@ -34,13 +40,17 @@ describe('integration: add and remove resources', () => {
expect(memories, 'memories should exist').toBeDefined();
const found = memories!.some((m: Record<string, unknown>) => m.name === memoryName);
expect(found, `Memory "${memoryName}" should be in config`).toBe(true);

// Verify telemetry
telemetry.assertMetricEmitted({ command: 'add.memory', exit_reason: 'success' });
});

it('adds a memory with EPISODIC strategy and verifies reflectionNamespaces', async () => {
const episodicMemName = `EpiMem${Date.now().toString().slice(-6)}`;
const result = await runCLI(
['add', 'memory', '--name', episodicMemName, '--strategies', 'EPISODIC', '--json'],
project.projectPath
project.projectPath,
{ env: telemetry.env }
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
Expand All@@ -61,6 +71,14 @@ describe('integration: add and remove resources', () => {
expect(episodic!.reflectionNamespaces, 'Should have reflectionNamespaces').toBeDefined();
expect(episodic!.reflectionNamespaces!.length).toBeGreaterThan(0);

// Verify telemetry
telemetry.assertMetricEmitted({
command: 'add.memory',
exit_reason: 'success',
strategy_count: '1',
strategy_episodic: 'true',
});

// Clean up
await runCLI(['remove', 'memory', '--name', episodicMemName, '--json'], project.projectPath);
});
Expand All@@ -86,7 +104,8 @@ describe('integration: add and remove resources', () => {
it('adds a credential resource', async () => {
const result = await runCLI(
['add', 'credential', '--name', credentialName, '--api-key', 'test-key-integ-123', '--json'],
project.projectPath
project.projectPath,
{ env: telemetry.env }
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
Expand All@@ -99,6 +118,13 @@ describe('integration: add and remove resources', () => {
expect(credentials, 'credentials should exist').toBeDefined();
const found = credentials!.some((c: Record<string, unknown>) => c.name === credentialName);
expect(found, `Credential "${credentialName}" should be in config`).toBe(true);

// Verify telemetry
telemetry.assertMetricEmitted({
command: 'add.credential',
exit_reason: 'success',
credential_type: 'api-key',
});
});

it('removes the credential resource', async () => {
Expand All@@ -115,4 +141,30 @@ describe('integration: add and remove resources', () => {
expect(found, `Credential "${credentialName}" should be removed from config`).toBe(false);
});
});

describe('policy-engine', () => {
const engineName = `TestEngine${Date.now().toString().slice(-6)}`;

it('adds a policy engine resource', async () => {
const result = await runCLI(['add', 'policy-engine', '--name', engineName, '--json'], project.projectPath, {
env: telemetry.env,
});

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);

telemetry.assertMetricEmitted({
command: 'add.policy-engine',
exit_reason: 'success',
attach_gateway_count: '0',
});
});

it('removes the policy engine resource', async () => {
const result = await runCLI(['remove', 'policy-engine', '--name', engineName, '--json'], project.projectPath);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
});
});
});
2 changes: 1 addition & 1 deletion integ-tests/create-no-agent.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,7 @@ describe('integration: create without agent', () => {

it.skipIf(!hasNpm || !hasGit)('creates project with real npm install and git init', async () => {
const name = `NoAgent${Date.now().toString().slice(-6)}`;
const result = await runCLI(['create', '--name', name, '--no-agent', '--json'], testDir, false);
const result = await runCLI(['create', '--name', name, '--no-agent', '--json'], testDir, { skipInstall: false });

expect(result.exitCode, `stderr: ${result.stderr}`).toBe(0);

Expand Down
2 changes: 1 addition & 1 deletion integ-tests/create-with-agent.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,7 +49,7 @@ describe('integration: create with Python agent', () => {
'--json',
],
testDir,
false
{ skipInstall: false }
);

expect(result.exitCode, `stderr: ${result.stderr}`).toBe(0);
Expand Down
2 changes: 1 addition & 1 deletion integ-tests/dev-server.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,7 +60,7 @@ describe('integration: dev server', () => {
'--json',
],
testDir,
false
{ skipInstall: false }
);

if (result.exitCode === 0) {
Expand Down
47 changes: 20 additions & 27 deletions integ-tests/help.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
import { spawnAndCollect } from '../src/test-utils/cli-runner.js';
import { runCLI } from '../src/test-utils/index.js';
import { createTelemetryHelper } from '../src/test-utils/telemetry-helper.js';
import { readdirSync } from 'node:fs';
import { mkdir, readFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { afterAll, describe, expect, it } from 'vitest';

const COMMANDS = [
'create',
Expand DownExpand Up@@ -45,52 +44,46 @@ describe('CLI help', () => {
});

describe('help modes telemetry', () => {
let testConfigDir: string;
const telemetry = createTelemetryHelper();
const cliPath = join(__dirname, '..', 'dist', 'cli', 'index.mjs');

beforeAll(async () => {
testConfigDir = join(tmpdir(), `agentcore-help-telemetry-${Date.now()}`);
await mkdir(testConfigDir, { recursive: true });
});
afterAll(() => rm(testConfigDir, { recursive: true, force: true }));
afterAll(() => telemetry.destroy());

function run(args: string[], extraEnv: Record<string, string> = {}) {
return spawnAndCollect('node', [cliPath, ...args], tmpdir(), {
return spawnAndCollect('node', [cliPath, ...args], process.cwd(), {
AGENTCORE_SKIP_INSTALL: '1',
AGENTCORE_CONFIG_DIR: testConfigDir,
...telemetry.env,
...extraEnv,
});
}

it('writes JSONL audit file when audit is enabled via env var', async () => {
const result = await run(['help', 'modes'], { AGENTCORE_TELEMETRY_AUDIT: '1' });
const result = await run(['help', 'modes']);
expect(result.exitCode).toBe(0);

const telemetryDir = join(testConfigDir, 'telemetry');
const files = readdirSync(telemetryDir).filter(f => f.startsWith('help-'));
expect(files).toHaveLength(1);

const content = await readFile(join(telemetryDir, files[0]!), 'utf-8');
const entry = JSON.parse(content.trim());
expect(entry.attrs).toMatchObject({
'service.name': 'agentcore-cli',
'agentcore-cli.mode': 'cli',
const entries = telemetry.readEntries();
expect(entries).toHaveLength(1);
telemetry.assertMetricEmitted({
command_group: 'help',
command: 'help.modes',
exit_reason: 'success',
});
expect(entry.attrs['agentcore-cli.session_id']).toBeDefined();
expect(entry.attrs['os.type']).toBeDefined();
expect(entry.value).toBeGreaterThanOrEqual(0);
expect(entries[0]!.attrs['agentcore-cli.session_id']).toBeDefined();
expect(entries[0]!.attrs['os.type']).toBeDefined();
expect(entries[0]!.value).toBeGreaterThanOrEqual(0);
});

it('does not write audit file when audit is not enabled', async () => {
const telemetryDir = join(testConfigDir, 'telemetry');
await rm(telemetryDir, { recursive: true, force: true });
telemetry.clearEntries();

const result = await run(['help', 'modes']);
const noAuditCliPath = join(__dirname, '..', 'dist', 'cli', 'index.mjs');
const result = await spawnAndCollect('node', [noAuditCliPath, 'help', 'modes'], process.cwd(), {
AGENTCORE_SKIP_INSTALL: '1',
AGENTCORE_CONFIG_DIR: telemetry.dir,
});
expect(result.exitCode).toBe(0);

const telemetryDir = join(telemetry.dir, 'telemetry');
try {
const files = readdirSync(telemetryDir);
expect(files).toHaveLength(0);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2a6caaf
feat: wire telemetry withCommandRun into all add.* commands
Hweinstock Apr 29, 2026
a0eb4ec
refactor: extract cliCommandRun helper, apply to all add.* primitives
Hweinstock Apr 29, 2026
b102b94
test: add audit file assertions for all add.* telemetry
Hweinstock Apr 29, 2026
72ab9d3
test: add telemetry audit assertions to existing add integ tests
Hweinstock Apr 29, 2026
e521dbc
refactor: extract shared audit test utils into src/test-utils/audit.ts
Hweinstock Apr 29, 2026
f152b0c
fix: address review feedback — guard telemetry init, replaceAll, unkn…
Hweinstock Apr 29, 2026
f7003ca
fix: AgentPrimitive TUI try/catch, standardize uses safeParse
Hweinstock Apr 29, 2026
091264c
refactor: extract standalone assertTelemetry helper
Hweinstock Apr 30, 2026
8aecebf
refactor: rename audit.ts to telemetry-helper.ts, clarify method names
Hweinstock Apr 30, 2026
8e4a229
refactor: move assertTelemetry into TelemetryHelper as assertMetricEm…
Hweinstock Apr 30, 2026
445a448
feat: add telemetry to TUI add paths via withAddTelemetry
Hweinstock Apr 30, 2026
c0af1ea
fix: review feedback — withAddTelemetry safety, standardize handles u…
Hweinstock Apr 30, 2026
db7300b
fix: remove unnecessary type assertion
Hweinstock Apr 30, 2026
de7c6df
fix: address review — document standardize cast, add policy-engine + …
Hweinstock Apr 30, 2026
9087af0
refactor: centralize gateway target type mapping in common-shapes
Hweinstock Apr 30, 2026
35a0531
fix: preserve original function error with telemetry wrapper
Hweinstock Apr 30, 2026
0d09e63
refactor: extract telemetryAttrs into a single line
Hweinstock Apr 30, 2026
3d948c4
feat: wire up telemetry for addAgent
Hweinstock Apr 30, 2026
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
2 changes: 1 addition & 1 deletion e2e-tests/byo-custom-jwt.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,7 +48,7 @@ const region = process.env.AWS_REGION ?? 'us-east-1';
* Run the local CLI build without skipping install (needed for deploy).
*/
function runLocalCLI(args: string[], cwd: string): Promise<RunResult> {
return runCLI(args, cwd, /* skipInstall */ false);
return runCLI(args, cwd, { skipInstall: false });
}

describe.sequential('e2e: BYO agent with CUSTOM_JWT auth', () => {
Expand Down
58 changes: 55 additions & 3 deletions integ-tests/add-remove-resources.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
import { createTestProject, readProjectConfig, runCLI } from '../src/test-utils/index.js';
import type { TestProject } from '../src/test-utils/index.js';
import { createTelemetryHelper } from '../src/test-utils/telemetry-helper.js';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

const telemetry = createTelemetryHelper();

describe('integration: add and remove resources', () => {
let project: TestProject;

Expand All@@ -16,13 +19,16 @@ describe('integration: add and remove resources', () => {

afterAll(async () => {
await project.cleanup();
telemetry.destroy();
});

describe('memory lifecycle', () => {
const memoryName = `IntegMem${Date.now().toString().slice(-6)}`;

it('adds a memory resource', async () => {
const result = await runCLI(['add', 'memory', '--name', memoryName, '--json'], project.projectPath);
const result = await runCLI(['add', 'memory', '--name', memoryName, '--json'], project.projectPath, {
env: telemetry.env,
});

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
Expand All@@ -34,13 +40,17 @@ describe('integration: add and remove resources', () => {
expect(memories, 'memories should exist').toBeDefined();
const found = memories!.some((m: Record<string, unknown>) => m.name === memoryName);
expect(found, `Memory "${memoryName}" should be in config`).toBe(true);

// Verify telemetry
telemetry.assertMetricEmitted({ command: 'add.memory', exit_reason: 'success' });
});

it('adds a memory with EPISODIC strategy and verifies reflectionNamespaces', async () => {
const episodicMemName = `EpiMem${Date.now().toString().slice(-6)}`;
const result = await runCLI(
['add', 'memory', '--name', episodicMemName, '--strategies', 'EPISODIC', '--json'],
project.projectPath
project.projectPath,
{ env: telemetry.env }
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
Expand All@@ -61,6 +71,14 @@ describe('integration: add and remove resources', () => {
expect(episodic!.reflectionNamespaces, 'Should have reflectionNamespaces').toBeDefined();
expect(episodic!.reflectionNamespaces!.length).toBeGreaterThan(0);

// Verify telemetry
telemetry.assertMetricEmitted({
command: 'add.memory',
exit_reason: 'success',
strategy_count: '1',
strategy_episodic: 'true',
});

// Clean up
await runCLI(['remove', 'memory', '--name', episodicMemName, '--json'], project.projectPath);
});
Expand All@@ -86,7 +104,8 @@ describe('integration: add and remove resources', () => {
it('adds a credential resource', async () => {
const result = await runCLI(
['add', 'credential', '--name', credentialName, '--api-key', 'test-key-integ-123', '--json'],
project.projectPath
project.projectPath,
{ env: telemetry.env }
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
Expand All@@ -99,6 +118,13 @@ describe('integration: add and remove resources', () => {
expect(credentials, 'credentials should exist').toBeDefined();
const found = credentials!.some((c: Record<string, unknown>) => c.name === credentialName);
expect(found, `Credential "${credentialName}" should be in config`).toBe(true);

// Verify telemetry
telemetry.assertMetricEmitted({
command: 'add.credential',
exit_reason: 'success',
credential_type: 'api-key',
});
});

it('removes the credential resource', async () => {
Expand All@@ -115,4 +141,30 @@ describe('integration: add and remove resources', () => {
expect(found, `Credential "${credentialName}" should be removed from config`).toBe(false);
});
});

describe('policy-engine', () => {
const engineName = `TestEngine${Date.now().toString().slice(-6)}`;

it('adds a policy engine resource', async () => {
const result = await runCLI(['add', 'policy-engine', '--name', engineName, '--json'], project.projectPath, {
env: telemetry.env,
});

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);

telemetry.assertMetricEmitted({
command: 'add.policy-engine',
exit_reason: 'success',
attach_gateway_count: '0',
});
});

it('removes the policy engine resource', async () => {
const result = await runCLI(['remove', 'policy-engine', '--name', engineName, '--json'], project.projectPath);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
});
});
});
2 changes: 1 addition & 1 deletion integ-tests/create-no-agent.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,7 @@ describe('integration: create without agent', () => {

it.skipIf(!hasNpm || !hasGit)('creates project with real npm install and git init', async () => {
const name = `NoAgent${Date.now().toString().slice(-6)}`;
const result = await runCLI(['create', '--name', name, '--no-agent', '--json'], testDir, false);
const result = await runCLI(['create', '--name', name, '--no-agent', '--json'], testDir, { skipInstall: false });

expect(result.exitCode, `stderr: ${result.stderr}`).toBe(0);

Expand Down
2 changes: 1 addition & 1 deletion integ-tests/create-with-agent.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,7 +49,7 @@ describe('integration: create with Python agent', () => {
'--json',
],
testDir,
false
{ skipInstall: false }
);

expect(result.exitCode, `stderr: ${result.stderr}`).toBe(0);
Expand Down
2 changes: 1 addition & 1 deletion integ-tests/dev-server.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,7 +60,7 @@ describe('integration: dev server', () => {
'--json',
],
testDir,
false
{ skipInstall: false }
);

if (result.exitCode === 0) {
Expand Down
47 changes: 20 additions & 27 deletions integ-tests/help.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
import { spawnAndCollect } from '../src/test-utils/cli-runner.js';
import { runCLI } from '../src/test-utils/index.js';
import { createTelemetryHelper } from '../src/test-utils/telemetry-helper.js';
import { readdirSync } from 'node:fs';
import { mkdir, readFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { afterAll, describe, expect, it } from 'vitest';

const COMMANDS = [
'create',
Expand DownExpand Up@@ -45,52 +44,46 @@ describe('CLI help', () => {
});

describe('help modes telemetry', () => {
let testConfigDir: string;
const telemetry = createTelemetryHelper();
const cliPath = join(__dirname, '..', 'dist', 'cli', 'index.mjs');

beforeAll(async () => {
testConfigDir = join(tmpdir(), `agentcore-help-telemetry-${Date.now()}`);
await mkdir(testConfigDir, { recursive: true });
});
afterAll(() => rm(testConfigDir, { recursive: true, force: true }));
afterAll(() => telemetry.destroy());

function run(args: string[], extraEnv: Record<string, string> = {}) {
return spawnAndCollect('node', [cliPath, ...args], tmpdir(), {
return spawnAndCollect('node', [cliPath, ...args], process.cwd(), {
AGENTCORE_SKIP_INSTALL: '1',
AGENTCORE_CONFIG_DIR: testConfigDir,
...telemetry.env,
...extraEnv,
});
}

it('writes JSONL audit file when audit is enabled via env var', async () => {
const result = await run(['help', 'modes'], { AGENTCORE_TELEMETRY_AUDIT: '1' });
const result = await run(['help', 'modes']);
expect(result.exitCode).toBe(0);

const telemetryDir = join(testConfigDir, 'telemetry');
const files = readdirSync(telemetryDir).filter(f => f.startsWith('help-'));
expect(files).toHaveLength(1);

const content = await readFile(join(telemetryDir, files[0]!), 'utf-8');
const entry = JSON.parse(content.trim());
expect(entry.attrs).toMatchObject({
'service.name': 'agentcore-cli',
'agentcore-cli.mode': 'cli',
const entries = telemetry.readEntries();
expect(entries).toHaveLength(1);
telemetry.assertMetricEmitted({
command_group: 'help',
command: 'help.modes',
exit_reason: 'success',
});
expect(entry.attrs['agentcore-cli.session_id']).toBeDefined();
expect(entry.attrs['os.type']).toBeDefined();
expect(entry.value).toBeGreaterThanOrEqual(0);
expect(entries[0]!.attrs['agentcore-cli.session_id']).toBeDefined();
expect(entries[0]!.attrs['os.type']).toBeDefined();
expect(entries[0]!.value).toBeGreaterThanOrEqual(0);
});

it('does not write audit file when audit is not enabled', async () => {
const telemetryDir = join(testConfigDir, 'telemetry');
await rm(telemetryDir, { recursive: true, force: true });
telemetry.clearEntries();

const result = await run(['help', 'modes']);
const noAuditCliPath = join(__dirname, '..', 'dist', 'cli', 'index.mjs');
const result = await spawnAndCollect('node', [noAuditCliPath, 'help', 'modes'], process.cwd(), {
AGENTCORE_SKIP_INSTALL: '1',
AGENTCORE_CONFIG_DIR: telemetry.dir,
});
expect(result.exitCode).toBe(0);

const telemetryDir = join(telemetry.dir, 'telemetry');
try {
const files = readdirSync(telemetryDir);
expect(files).toHaveLength(0);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2a6caaf
feat: wire telemetry withCommandRun into all add.* commands
Hweinstock Apr 29, 2026
a0eb4ec
refactor: extract cliCommandRun helper, apply to all add.* primitives
Hweinstock Apr 29, 2026
b102b94
test: add audit file assertions for all add.* telemetry
Hweinstock Apr 29, 2026
72ab9d3
test: add telemetry audit assertions to existing add integ tests
Hweinstock Apr 29, 2026
e521dbc
refactor: extract shared audit test utils into src/test-utils/audit.ts
Hweinstock Apr 29, 2026
f152b0c
fix: address review feedback — guard telemetry init, replaceAll, unkn…
Hweinstock Apr 29, 2026
f7003ca
fix: AgentPrimitive TUI try/catch, standardize uses safeParse
Hweinstock Apr 29, 2026
091264c
refactor: extract standalone assertTelemetry helper
Hweinstock Apr 30, 2026
8aecebf
refactor: rename audit.ts to telemetry-helper.ts, clarify method names
Hweinstock Apr 30, 2026
8e4a229
refactor: move assertTelemetry into TelemetryHelper as assertMetricEm…
Hweinstock Apr 30, 2026
445a448
feat: add telemetry to TUI add paths via withAddTelemetry
Hweinstock Apr 30, 2026
c0af1ea
fix: review feedback — withAddTelemetry safety, standardize handles u…
Hweinstock Apr 30, 2026
db7300b
fix: remove unnecessary type assertion
Hweinstock Apr 30, 2026
de7c6df
fix: address review — document standardize cast, add policy-engine + …
Hweinstock Apr 30, 2026
9087af0
refactor: centralize gateway target type mapping in common-shapes
Hweinstock Apr 30, 2026
35a0531
fix: preserve original function error with telemetry wrapper
Hweinstock Apr 30, 2026
0d09e63
refactor: extract telemetryAttrs into a single line
Hweinstock Apr 30, 2026
3d948c4
feat: wire up telemetry for addAgent
Hweinstock Apr 30, 2026
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
2 changes: 1 addition & 1 deletion e2e-tests/byo-custom-jwt.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,7 +48,7 @@ const region = process.env.AWS_REGION ?? 'us-east-1';
* Run the local CLI build without skipping install (needed for deploy).
*/
function runLocalCLI(args: string[], cwd: string): Promise<RunResult> {
return runCLI(args, cwd, /* skipInstall */ false);
return runCLI(args, cwd, { skipInstall: false });
}

describe.sequential('e2e: BYO agent with CUSTOM_JWT auth', () => {
Expand Down
58 changes: 55 additions & 3 deletions integ-tests/add-remove-resources.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
import { createTestProject, readProjectConfig, runCLI } from '../src/test-utils/index.js';
import type { TestProject } from '../src/test-utils/index.js';
import { createTelemetryHelper } from '../src/test-utils/telemetry-helper.js';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

const telemetry = createTelemetryHelper();

describe('integration: add and remove resources', () => {
let project: TestProject;

Expand All@@ -16,13 +19,16 @@ describe('integration: add and remove resources', () => {

afterAll(async () => {
await project.cleanup();
telemetry.destroy();
});

describe('memory lifecycle', () => {
const memoryName = `IntegMem${Date.now().toString().slice(-6)}`;

it('adds a memory resource', async () => {
const result = await runCLI(['add', 'memory', '--name', memoryName, '--json'], project.projectPath);
const result = await runCLI(['add', 'memory', '--name', memoryName, '--json'], project.projectPath, {
env: telemetry.env,
});

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
Expand All@@ -34,13 +40,17 @@ describe('integration: add and remove resources', () => {
expect(memories, 'memories should exist').toBeDefined();
const found = memories!.some((m: Record<string, unknown>) => m.name === memoryName);
expect(found, `Memory "${memoryName}" should be in config`).toBe(true);

// Verify telemetry
telemetry.assertMetricEmitted({ command: 'add.memory', exit_reason: 'success' });
});

it('adds a memory with EPISODIC strategy and verifies reflectionNamespaces', async () => {
const episodicMemName = `EpiMem${Date.now().toString().slice(-6)}`;
const result = await runCLI(
['add', 'memory', '--name', episodicMemName, '--strategies', 'EPISODIC', '--json'],
project.projectPath
project.projectPath,
{ env: telemetry.env }
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
Expand All@@ -61,6 +71,14 @@ describe('integration: add and remove resources', () => {
expect(episodic!.reflectionNamespaces, 'Should have reflectionNamespaces').toBeDefined();
expect(episodic!.reflectionNamespaces!.length).toBeGreaterThan(0);

// Verify telemetry
telemetry.assertMetricEmitted({
command: 'add.memory',
exit_reason: 'success',
strategy_count: '1',
strategy_episodic: 'true',
});

// Clean up
await runCLI(['remove', 'memory', '--name', episodicMemName, '--json'], project.projectPath);
});
Expand All@@ -86,7 +104,8 @@ describe('integration: add and remove resources', () => {
it('adds a credential resource', async () => {
const result = await runCLI(
['add', 'credential', '--name', credentialName, '--api-key', 'test-key-integ-123', '--json'],
project.projectPath
project.projectPath,
{ env: telemetry.env }
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
Expand All@@ -99,6 +118,13 @@ describe('integration: add and remove resources', () => {
expect(credentials, 'credentials should exist').toBeDefined();
const found = credentials!.some((c: Record<string, unknown>) => c.name === credentialName);
expect(found, `Credential "${credentialName}" should be in config`).toBe(true);

// Verify telemetry
telemetry.assertMetricEmitted({
command: 'add.credential',
exit_reason: 'success',
credential_type: 'api-key',
});
});

it('removes the credential resource', async () => {
Expand All@@ -115,4 +141,30 @@ describe('integration: add and remove resources', () => {
expect(found, `Credential "${credentialName}" should be removed from config`).toBe(false);
});
});

describe('policy-engine', () => {
const engineName = `TestEngine${Date.now().toString().slice(-6)}`;

it('adds a policy engine resource', async () => {
const result = await runCLI(['add', 'policy-engine', '--name', engineName, '--json'], project.projectPath, {
env: telemetry.env,
});

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);

telemetry.assertMetricEmitted({
command: 'add.policy-engine',
exit_reason: 'success',
attach_gateway_count: '0',
});
});

it('removes the policy engine resource', async () => {
const result = await runCLI(['remove', 'policy-engine', '--name', engineName, '--json'], project.projectPath);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
});
});
});
2 changes: 1 addition & 1 deletion integ-tests/create-no-agent.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,7 @@ describe('integration: create without agent', () => {

it.skipIf(!hasNpm || !hasGit)('creates project with real npm install and git init', async () => {
const name = `NoAgent${Date.now().toString().slice(-6)}`;
const result = await runCLI(['create', '--name', name, '--no-agent', '--json'], testDir, false);
const result = await runCLI(['create', '--name', name, '--no-agent', '--json'], testDir, { skipInstall: false });

expect(result.exitCode, `stderr: ${result.stderr}`).toBe(0);

Expand Down
2 changes: 1 addition & 1 deletion integ-tests/create-with-agent.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,7 +49,7 @@ describe('integration: create with Python agent', () => {
'--json',
],
testDir,
false
{ skipInstall: false }
);

expect(result.exitCode, `stderr: ${result.stderr}`).toBe(0);
Expand Down
2 changes: 1 addition & 1 deletion integ-tests/dev-server.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,7 +60,7 @@ describe('integration: dev server', () => {
'--json',
],
testDir,
false
{ skipInstall: false }
);

if (result.exitCode === 0) {
Expand Down
47 changes: 20 additions & 27 deletions integ-tests/help.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
import { spawnAndCollect } from '../src/test-utils/cli-runner.js';
import { runCLI } from '../src/test-utils/index.js';
import { createTelemetryHelper } from '../src/test-utils/telemetry-helper.js';
import { readdirSync } from 'node:fs';
import { mkdir, readFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { afterAll, describe, expect, it } from 'vitest';

const COMMANDS = [
'create',
Expand DownExpand Up@@ -45,52 +44,46 @@ describe('CLI help', () => {
});

describe('help modes telemetry', () => {
let testConfigDir: string;
const telemetry = createTelemetryHelper();
const cliPath = join(__dirname, '..', 'dist', 'cli', 'index.mjs');

beforeAll(async () => {
testConfigDir = join(tmpdir(), `agentcore-help-telemetry-${Date.now()}`);
await mkdir(testConfigDir, { recursive: true });
});
afterAll(() => rm(testConfigDir, { recursive: true, force: true }));
afterAll(() => telemetry.destroy());

function run(args: string[], extraEnv: Record<string, string> = {}) {
return spawnAndCollect('node', [cliPath, ...args], tmpdir(), {
return spawnAndCollect('node', [cliPath, ...args], process.cwd(), {
AGENTCORE_SKIP_INSTALL: '1',
AGENTCORE_CONFIG_DIR: testConfigDir,
...telemetry.env,
...extraEnv,
});
}

it('writes JSONL audit file when audit is enabled via env var', async () => {
const result = await run(['help', 'modes'], { AGENTCORE_TELEMETRY_AUDIT: '1' });
const result = await run(['help', 'modes']);
expect(result.exitCode).toBe(0);

const telemetryDir = join(testConfigDir, 'telemetry');
const files = readdirSync(telemetryDir).filter(f => f.startsWith('help-'));
expect(files).toHaveLength(1);

const content = await readFile(join(telemetryDir, files[0]!), 'utf-8');
const entry = JSON.parse(content.trim());
expect(entry.attrs).toMatchObject({
'service.name': 'agentcore-cli',
'agentcore-cli.mode': 'cli',
const entries = telemetry.readEntries();
expect(entries).toHaveLength(1);
telemetry.assertMetricEmitted({
command_group: 'help',
command: 'help.modes',
exit_reason: 'success',
});
expect(entry.attrs['agentcore-cli.session_id']).toBeDefined();
expect(entry.attrs['os.type']).toBeDefined();
expect(entry.value).toBeGreaterThanOrEqual(0);
expect(entries[0]!.attrs['agentcore-cli.session_id']).toBeDefined();
expect(entries[0]!.attrs['os.type']).toBeDefined();
expect(entries[0]!.value).toBeGreaterThanOrEqual(0);
});

it('does not write audit file when audit is not enabled', async () => {
const telemetryDir = join(testConfigDir, 'telemetry');
await rm(telemetryDir, { recursive: true, force: true });
telemetry.clearEntries();

const result = await run(['help', 'modes']);
const noAuditCliPath = join(__dirname, '..', 'dist', 'cli', 'index.mjs');
const result = await spawnAndCollect('node', [noAuditCliPath, 'help', 'modes'], process.cwd(), {
AGENTCORE_SKIP_INSTALL: '1',
AGENTCORE_CONFIG_DIR: telemetry.dir,
});
expect(result.exitCode).toBe(0);

const telemetryDir = join(telemetry.dir, 'telemetry');
try {
const files = readdirSync(telemetryDir);
expect(files).toHaveLength(0);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2a6caaf
feat: wire telemetry withCommandRun into all add.* commands
Hweinstock Apr 29, 2026
a0eb4ec
refactor: extract cliCommandRun helper, apply to all add.* primitives
Hweinstock Apr 29, 2026
b102b94
test: add audit file assertions for all add.* telemetry
Hweinstock Apr 29, 2026
72ab9d3
test: add telemetry audit assertions to existing add integ tests
Hweinstock Apr 29, 2026
e521dbc
refactor: extract shared audit test utils into src/test-utils/audit.ts
Hweinstock Apr 29, 2026
f152b0c
fix: address review feedback — guard telemetry init, replaceAll, unkn…
Hweinstock Apr 29, 2026
f7003ca
fix: AgentPrimitive TUI try/catch, standardize uses safeParse
Hweinstock Apr 29, 2026
091264c
refactor: extract standalone assertTelemetry helper
Hweinstock Apr 30, 2026
8aecebf
refactor: rename audit.ts to telemetry-helper.ts, clarify method names
Hweinstock Apr 30, 2026
8e4a229
refactor: move assertTelemetry into TelemetryHelper as assertMetricEm…
Hweinstock Apr 30, 2026
445a448
feat: add telemetry to TUI add paths via withAddTelemetry
Hweinstock Apr 30, 2026
c0af1ea
fix: review feedback — withAddTelemetry safety, standardize handles u…
Hweinstock Apr 30, 2026
db7300b
fix: remove unnecessary type assertion
Hweinstock Apr 30, 2026
de7c6df
fix: address review — document standardize cast, add policy-engine + …
Hweinstock Apr 30, 2026
9087af0
refactor: centralize gateway target type mapping in common-shapes
Hweinstock Apr 30, 2026
35a0531
fix: preserve original function error with telemetry wrapper
Hweinstock Apr 30, 2026
0d09e63
refactor: extract telemetryAttrs into a single line
Hweinstock Apr 30, 2026
3d948c4
feat: wire up telemetry for addAgent
Hweinstock Apr 30, 2026
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
2 changes: 1 addition & 1 deletion e2e-tests/byo-custom-jwt.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,7 +48,7 @@ const region = process.env.AWS_REGION ?? 'us-east-1';
* Run the local CLI build without skipping install (needed for deploy).
*/
function runLocalCLI(args: string[], cwd: string): Promise<RunResult> {
return runCLI(args, cwd, /* skipInstall */ false);
return runCLI(args, cwd, { skipInstall: false });
}

describe.sequential('e2e: BYO agent with CUSTOM_JWT auth', () => {
Expand Down
58 changes: 55 additions & 3 deletions integ-tests/add-remove-resources.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
import { createTestProject, readProjectConfig, runCLI } from '../src/test-utils/index.js';
import type { TestProject } from '../src/test-utils/index.js';
import { createTelemetryHelper } from '../src/test-utils/telemetry-helper.js';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

const telemetry = createTelemetryHelper();

describe('integration: add and remove resources', () => {
let project: TestProject;

Expand All@@ -16,13 +19,16 @@ describe('integration: add and remove resources', () => {

afterAll(async () => {
await project.cleanup();
telemetry.destroy();
});

describe('memory lifecycle', () => {
const memoryName = `IntegMem${Date.now().toString().slice(-6)}`;

it('adds a memory resource', async () => {
const result = await runCLI(['add', 'memory', '--name', memoryName, '--json'], project.projectPath);
const result = await runCLI(['add', 'memory', '--name', memoryName, '--json'], project.projectPath, {
env: telemetry.env,
});

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
Expand All@@ -34,13 +40,17 @@ describe('integration: add and remove resources', () => {
expect(memories, 'memories should exist').toBeDefined();
const found = memories!.some((m: Record<string, unknown>) => m.name === memoryName);
expect(found, `Memory "${memoryName}" should be in config`).toBe(true);

// Verify telemetry
telemetry.assertMetricEmitted({ command: 'add.memory', exit_reason: 'success' });
});

it('adds a memory with EPISODIC strategy and verifies reflectionNamespaces', async () => {
const episodicMemName = `EpiMem${Date.now().toString().slice(-6)}`;
const result = await runCLI(
['add', 'memory', '--name', episodicMemName, '--strategies', 'EPISODIC', '--json'],
project.projectPath
project.projectPath,
{ env: telemetry.env }
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
Expand All@@ -61,6 +71,14 @@ describe('integration: add and remove resources', () => {
expect(episodic!.reflectionNamespaces, 'Should have reflectionNamespaces').toBeDefined();
expect(episodic!.reflectionNamespaces!.length).toBeGreaterThan(0);

// Verify telemetry
telemetry.assertMetricEmitted({
command: 'add.memory',
exit_reason: 'success',
strategy_count: '1',
strategy_episodic: 'true',
});

// Clean up
await runCLI(['remove', 'memory', '--name', episodicMemName, '--json'], project.projectPath);
});
Expand All@@ -86,7 +104,8 @@ describe('integration: add and remove resources', () => {
it('adds a credential resource', async () => {
const result = await runCLI(
['add', 'credential', '--name', credentialName, '--api-key', 'test-key-integ-123', '--json'],
project.projectPath
project.projectPath,
{ env: telemetry.env }
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
Expand All@@ -99,6 +118,13 @@ describe('integration: add and remove resources', () => {
expect(credentials, 'credentials should exist').toBeDefined();
const found = credentials!.some((c: Record<string, unknown>) => c.name === credentialName);
expect(found, `Credential "${credentialName}" should be in config`).toBe(true);

// Verify telemetry
telemetry.assertMetricEmitted({
command: 'add.credential',
exit_reason: 'success',
credential_type: 'api-key',
});
});

it('removes the credential resource', async () => {
Expand All@@ -115,4 +141,30 @@ describe('integration: add and remove resources', () => {
expect(found, `Credential "${credentialName}" should be removed from config`).toBe(false);
});
});

describe('policy-engine', () => {
const engineName = `TestEngine${Date.now().toString().slice(-6)}`;

it('adds a policy engine resource', async () => {
const result = await runCLI(['add', 'policy-engine', '--name', engineName, '--json'], project.projectPath, {
env: telemetry.env,
});

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);

telemetry.assertMetricEmitted({
command: 'add.policy-engine',
exit_reason: 'success',
attach_gateway_count: '0',
});
});

it('removes the policy engine resource', async () => {
const result = await runCLI(['remove', 'policy-engine', '--name', engineName, '--json'], project.projectPath);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
});
});
});
2 changes: 1 addition & 1 deletion integ-tests/create-no-agent.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,7 @@ describe('integration: create without agent', () => {

it.skipIf(!hasNpm || !hasGit)('creates project with real npm install and git init', async () => {
const name = `NoAgent${Date.now().toString().slice(-6)}`;
const result = await runCLI(['create', '--name', name, '--no-agent', '--json'], testDir, false);
const result = await runCLI(['create', '--name', name, '--no-agent', '--json'], testDir, { skipInstall: false });

expect(result.exitCode, `stderr: ${result.stderr}`).toBe(0);

Expand Down
2 changes: 1 addition & 1 deletion integ-tests/create-with-agent.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,7 +49,7 @@ describe('integration: create with Python agent', () => {
'--json',
],
testDir,
false
{ skipInstall: false }
);

expect(result.exitCode, `stderr: ${result.stderr}`).toBe(0);
Expand Down
2 changes: 1 addition & 1 deletion integ-tests/dev-server.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,7 +60,7 @@ describe('integration: dev server', () => {
'--json',
],
testDir,
false
{ skipInstall: false }
);

if (result.exitCode === 0) {
Expand Down
47 changes: 20 additions & 27 deletions integ-tests/help.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
import { spawnAndCollect } from '../src/test-utils/cli-runner.js';
import { runCLI } from '../src/test-utils/index.js';
import { createTelemetryHelper } from '../src/test-utils/telemetry-helper.js';
import { readdirSync } from 'node:fs';
import { mkdir, readFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { afterAll, describe, expect, it } from 'vitest';

const COMMANDS = [
'create',
Expand DownExpand Up@@ -45,52 +44,46 @@ describe('CLI help', () => {
});

describe('help modes telemetry', () => {
let testConfigDir: string;
const telemetry = createTelemetryHelper();
const cliPath = join(__dirname, '..', 'dist', 'cli', 'index.mjs');

beforeAll(async () => {
testConfigDir = join(tmpdir(), `agentcore-help-telemetry-${Date.now()}`);
await mkdir(testConfigDir, { recursive: true });
});
afterAll(() => rm(testConfigDir, { recursive: true, force: true }));
afterAll(() => telemetry.destroy());

function run(args: string[], extraEnv: Record<string, string> = {}) {
return spawnAndCollect('node', [cliPath, ...args], tmpdir(), {
return spawnAndCollect('node', [cliPath, ...args], process.cwd(), {
AGENTCORE_SKIP_INSTALL: '1',
AGENTCORE_CONFIG_DIR: testConfigDir,
...telemetry.env,
...extraEnv,
});
}

it('writes JSONL audit file when audit is enabled via env var', async () => {
const result = await run(['help', 'modes'], { AGENTCORE_TELEMETRY_AUDIT: '1' });
const result = await run(['help', 'modes']);
expect(result.exitCode).toBe(0);

const telemetryDir = join(testConfigDir, 'telemetry');
const files = readdirSync(telemetryDir).filter(f => f.startsWith('help-'));
expect(files).toHaveLength(1);

const content = await readFile(join(telemetryDir, files[0]!), 'utf-8');
const entry = JSON.parse(content.trim());
expect(entry.attrs).toMatchObject({
'service.name': 'agentcore-cli',
'agentcore-cli.mode': 'cli',
const entries = telemetry.readEntries();
expect(entries).toHaveLength(1);
telemetry.assertMetricEmitted({
command_group: 'help',
command: 'help.modes',
exit_reason: 'success',
});
expect(entry.attrs['agentcore-cli.session_id']).toBeDefined();
expect(entry.attrs['os.type']).toBeDefined();
expect(entry.value).toBeGreaterThanOrEqual(0);
expect(entries[0]!.attrs['agentcore-cli.session_id']).toBeDefined();
expect(entries[0]!.attrs['os.type']).toBeDefined();
expect(entries[0]!.value).toBeGreaterThanOrEqual(0);
});

it('does not write audit file when audit is not enabled', async () => {
const telemetryDir = join(testConfigDir, 'telemetry');
await rm(telemetryDir, { recursive: true, force: true });
telemetry.clearEntries();

const result = await run(['help', 'modes']);
const noAuditCliPath = join(__dirname, '..', 'dist', 'cli', 'index.mjs');
const result = await spawnAndCollect('node', [noAuditCliPath, 'help', 'modes'], process.cwd(), {
AGENTCORE_SKIP_INSTALL: '1',
AGENTCORE_CONFIG_DIR: telemetry.dir,
});
expect(result.exitCode).toBe(0);

const telemetryDir = join(telemetry.dir, 'telemetry');
try {
const files = readdirSync(telemetryDir);
expect(files).toHaveLength(0);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2a6caaf
feat: wire telemetry withCommandRun into all add.* commands
Hweinstock Apr 29, 2026
a0eb4ec
refactor: extract cliCommandRun helper, apply to all add.* primitives
Hweinstock Apr 29, 2026
b102b94
test: add audit file assertions for all add.* telemetry
Hweinstock Apr 29, 2026
72ab9d3
test: add telemetry audit assertions to existing add integ tests
Hweinstock Apr 29, 2026
e521dbc
refactor: extract shared audit test utils into src/test-utils/audit.ts
Hweinstock Apr 29, 2026
f152b0c
fix: address review feedback — guard telemetry init, replaceAll, unkn…
Hweinstock Apr 29, 2026
f7003ca
fix: AgentPrimitive TUI try/catch, standardize uses safeParse
Hweinstock Apr 29, 2026
091264c
refactor: extract standalone assertTelemetry helper
Hweinstock Apr 30, 2026
8aecebf
refactor: rename audit.ts to telemetry-helper.ts, clarify method names
Hweinstock Apr 30, 2026
8e4a229
refactor: move assertTelemetry into TelemetryHelper as assertMetricEm…
Hweinstock Apr 30, 2026
445a448
feat: add telemetry to TUI add paths via withAddTelemetry
Hweinstock Apr 30, 2026
c0af1ea
fix: review feedback — withAddTelemetry safety, standardize handles u…
Hweinstock Apr 30, 2026
db7300b
fix: remove unnecessary type assertion
Hweinstock Apr 30, 2026
de7c6df
fix: address review — document standardize cast, add policy-engine + …
Hweinstock Apr 30, 2026
9087af0
refactor: centralize gateway target type mapping in common-shapes
Hweinstock Apr 30, 2026
35a0531
fix: preserve original function error with telemetry wrapper
Hweinstock Apr 30, 2026
0d09e63
refactor: extract telemetryAttrs into a single line
Hweinstock Apr 30, 2026
3d948c4
feat: wire up telemetry for addAgent
Hweinstock Apr 30, 2026
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
2 changes: 1 addition & 1 deletion e2e-tests/byo-custom-jwt.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,7 +48,7 @@ const region = process.env.AWS_REGION ?? 'us-east-1';
* Run the local CLI build without skipping install (needed for deploy).
*/
function runLocalCLI(args: string[], cwd: string): Promise<RunResult> {
return runCLI(args, cwd, /* skipInstall */ false);
return runCLI(args, cwd, { skipInstall: false });
}

describe.sequential('e2e: BYO agent with CUSTOM_JWT auth', () => {
Expand Down
58 changes: 55 additions & 3 deletions integ-tests/add-remove-resources.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
import { createTestProject, readProjectConfig, runCLI } from '../src/test-utils/index.js';
import type { TestProject } from '../src/test-utils/index.js';
import { createTelemetryHelper } from '../src/test-utils/telemetry-helper.js';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

const telemetry = createTelemetryHelper();

describe('integration: add and remove resources', () => {
let project: TestProject;

Expand All@@ -16,13 +19,16 @@ describe('integration: add and remove resources', () => {

afterAll(async () => {
await project.cleanup();
telemetry.destroy();
});

describe('memory lifecycle', () => {
const memoryName = `IntegMem${Date.now().toString().slice(-6)}`;

it('adds a memory resource', async () => {
const result = await runCLI(['add', 'memory', '--name', memoryName, '--json'], project.projectPath);
const result = await runCLI(['add', 'memory', '--name', memoryName, '--json'], project.projectPath, {
env: telemetry.env,
});

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
Expand All@@ -34,13 +40,17 @@ describe('integration: add and remove resources', () => {
expect(memories, 'memories should exist').toBeDefined();
const found = memories!.some((m: Record<string, unknown>) => m.name === memoryName);
expect(found, `Memory "${memoryName}" should be in config`).toBe(true);

// Verify telemetry
telemetry.assertMetricEmitted({ command: 'add.memory', exit_reason: 'success' });
});

it('adds a memory with EPISODIC strategy and verifies reflectionNamespaces', async () => {
const episodicMemName = `EpiMem${Date.now().toString().slice(-6)}`;
const result = await runCLI(
['add', 'memory', '--name', episodicMemName, '--strategies', 'EPISODIC', '--json'],
project.projectPath
project.projectPath,
{ env: telemetry.env }
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
Expand All@@ -61,6 +71,14 @@ describe('integration: add and remove resources', () => {
expect(episodic!.reflectionNamespaces, 'Should have reflectionNamespaces').toBeDefined();
expect(episodic!.reflectionNamespaces!.length).toBeGreaterThan(0);

// Verify telemetry
telemetry.assertMetricEmitted({
command: 'add.memory',
exit_reason: 'success',
strategy_count: '1',
strategy_episodic: 'true',
});

// Clean up
await runCLI(['remove', 'memory', '--name', episodicMemName, '--json'], project.projectPath);
});
Expand All@@ -86,7 +104,8 @@ describe('integration: add and remove resources', () => {
it('adds a credential resource', async () => {
const result = await runCLI(
['add', 'credential', '--name', credentialName, '--api-key', 'test-key-integ-123', '--json'],
project.projectPath
project.projectPath,
{ env: telemetry.env }
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
Expand All@@ -99,6 +118,13 @@ describe('integration: add and remove resources', () => {
expect(credentials, 'credentials should exist').toBeDefined();
const found = credentials!.some((c: Record<string, unknown>) => c.name === credentialName);
expect(found, `Credential "${credentialName}" should be in config`).toBe(true);

// Verify telemetry
telemetry.assertMetricEmitted({
command: 'add.credential',
exit_reason: 'success',
credential_type: 'api-key',
});
});

it('removes the credential resource', async () => {
Expand All@@ -115,4 +141,30 @@ describe('integration: add and remove resources', () => {
expect(found, `Credential "${credentialName}" should be removed from config`).toBe(false);
});
});

describe('policy-engine', () => {
const engineName = `TestEngine${Date.now().toString().slice(-6)}`;

it('adds a policy engine resource', async () => {
const result = await runCLI(['add', 'policy-engine', '--name', engineName, '--json'], project.projectPath, {
env: telemetry.env,
});

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);

telemetry.assertMetricEmitted({
command: 'add.policy-engine',
exit_reason: 'success',
attach_gateway_count: '0',
});
});

it('removes the policy engine resource', async () => {
const result = await runCLI(['remove', 'policy-engine', '--name', engineName, '--json'], project.projectPath);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
});
});
});
2 changes: 1 addition & 1 deletion integ-tests/create-no-agent.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,7 @@ describe('integration: create without agent', () => {

it.skipIf(!hasNpm || !hasGit)('creates project with real npm install and git init', async () => {
const name = `NoAgent${Date.now().toString().slice(-6)}`;
const result = await runCLI(['create', '--name', name, '--no-agent', '--json'], testDir, false);
const result = await runCLI(['create', '--name', name, '--no-agent', '--json'], testDir, { skipInstall: false });

expect(result.exitCode, `stderr: ${result.stderr}`).toBe(0);

Expand Down
2 changes: 1 addition & 1 deletion integ-tests/create-with-agent.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,7 +49,7 @@ describe('integration: create with Python agent', () => {
'--json',
],
testDir,
false
{ skipInstall: false }
);

expect(result.exitCode, `stderr: ${result.stderr}`).toBe(0);
Expand Down
2 changes: 1 addition & 1 deletion integ-tests/dev-server.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,7 +60,7 @@ describe('integration: dev server', () => {
'--json',
],
testDir,
false
{ skipInstall: false }
);

if (result.exitCode === 0) {
Expand Down
47 changes: 20 additions & 27 deletions integ-tests/help.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
import { spawnAndCollect } from '../src/test-utils/cli-runner.js';
import { runCLI } from '../src/test-utils/index.js';
import { createTelemetryHelper } from '../src/test-utils/telemetry-helper.js';
import { readdirSync } from 'node:fs';
import { mkdir, readFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { afterAll, describe, expect, it } from 'vitest';

const COMMANDS = [
'create',
Expand DownExpand Up@@ -45,52 +44,46 @@ describe('CLI help', () => {
});

describe('help modes telemetry', () => {
let testConfigDir: string;
const telemetry = createTelemetryHelper();
const cliPath = join(__dirname, '..', 'dist', 'cli', 'index.mjs');

beforeAll(async () => {
testConfigDir = join(tmpdir(), `agentcore-help-telemetry-${Date.now()}`);
await mkdir(testConfigDir, { recursive: true });
});
afterAll(() => rm(testConfigDir, { recursive: true, force: true }));
afterAll(() => telemetry.destroy());

function run(args: string[], extraEnv: Record<string, string> = {}) {
return spawnAndCollect('node', [cliPath, ...args], tmpdir(), {
return spawnAndCollect('node', [cliPath, ...args], process.cwd(), {
AGENTCORE_SKIP_INSTALL: '1',
AGENTCORE_CONFIG_DIR: testConfigDir,
...telemetry.env,
...extraEnv,
});
}

it('writes JSONL audit file when audit is enabled via env var', async () => {
const result = await run(['help', 'modes'], { AGENTCORE_TELEMETRY_AUDIT: '1' });
const result = await run(['help', 'modes']);
expect(result.exitCode).toBe(0);

const telemetryDir = join(testConfigDir, 'telemetry');
const files = readdirSync(telemetryDir).filter(f => f.startsWith('help-'));
expect(files).toHaveLength(1);

const content = await readFile(join(telemetryDir, files[0]!), 'utf-8');
const entry = JSON.parse(content.trim());
expect(entry.attrs).toMatchObject({
'service.name': 'agentcore-cli',
'agentcore-cli.mode': 'cli',
const entries = telemetry.readEntries();
expect(entries).toHaveLength(1);
telemetry.assertMetricEmitted({
command_group: 'help',
command: 'help.modes',
exit_reason: 'success',
});
expect(entry.attrs['agentcore-cli.session_id']).toBeDefined();
expect(entry.attrs['os.type']).toBeDefined();
expect(entry.value).toBeGreaterThanOrEqual(0);
expect(entries[0]!.attrs['agentcore-cli.session_id']).toBeDefined();
expect(entries[0]!.attrs['os.type']).toBeDefined();
expect(entries[0]!.value).toBeGreaterThanOrEqual(0);
});

it('does not write audit file when audit is not enabled', async () => {
const telemetryDir = join(testConfigDir, 'telemetry');
await rm(telemetryDir, { recursive: true, force: true });
telemetry.clearEntries();

const result = await run(['help', 'modes']);
const noAuditCliPath = join(__dirname, '..', 'dist', 'cli', 'index.mjs');
const result = await spawnAndCollect('node', [noAuditCliPath, 'help', 'modes'], process.cwd(), {
AGENTCORE_SKIP_INSTALL: '1',
AGENTCORE_CONFIG_DIR: telemetry.dir,
});
expect(result.exitCode).toBe(0);

const telemetryDir = join(telemetry.dir, 'telemetry');
try {
const files = readdirSync(telemetryDir);
expect(files).toHaveLength(0);
Expand Down
Loading
Loading