mirror of
https://github.com/anthropics/claude-code.git
synced 2026-10-02 05:25:05 +08:00
telemetry, agents-md: the test plugins hook and call the collector stream by name, and stand in for telemetry through its events (#96930)
* telemetry: the test plugins a person installed hook and call the collector's stream by name * agents-md: the test plugin standing in for telemetry hooks its two events, adding the noun only where the engine has none
This commit is contained in:
+22
-13
@@ -1,29 +1,38 @@
|
||||
import type { Plugin } from 'claude-code/testing'
|
||||
|
||||
/**
|
||||
* A plugin standing in for the telemetry built-in: it adds the `telemetry`
|
||||
* noun at `engine.create` over the nouns beneath.
|
||||
* A plugin standing in for the telemetry built-in: it hooks `telemetry.log`
|
||||
* and `telemetry.mark` and answers them without going on.
|
||||
*
|
||||
* Its `log` and `mark` write the entry they are handed as one `$.ui.log`
|
||||
* line, `<op> <json>`, which startedOf keeps and rowsOf reads back.
|
||||
* Each writes the entry as the caller handed it (`to` left off) as one
|
||||
* `$.ui.log` line, `<op> <json>`, which startedOf keeps and rowsOf reads
|
||||
* back. Where the engine has no `telemetry` its engine.create step adds one.
|
||||
*/
|
||||
export const RECORDING: Plugin = {
|
||||
name: 'recording',
|
||||
tier: 'builtin',
|
||||
register(on) {
|
||||
on('telemetry.log', async ($, e) => {
|
||||
const { to: _to, ...entry } = e
|
||||
|
||||
await $.ui.log(`log ${JSON.stringify(entry)}`)
|
||||
|
||||
return { value: undefined }
|
||||
})
|
||||
|
||||
on('telemetry.mark', async ($, e) => {
|
||||
await $.ui.log(`mark ${JSON.stringify(e)}`)
|
||||
|
||||
return { value: undefined }
|
||||
})
|
||||
|
||||
on('engine.create', async ($, e, next) => {
|
||||
const beneath = await next(e)
|
||||
const built = { ...beneath }
|
||||
built.telemetry = {
|
||||
log: async entry => {
|
||||
await beneath.ui.log(`log ${JSON.stringify(entry)}`)
|
||||
},
|
||||
mark: async entry => {
|
||||
await beneath.ui.log(`mark ${JSON.stringify(entry)}`)
|
||||
},
|
||||
const added = {
|
||||
telemetry: { log: async () => undefined, mark: async () => undefined },
|
||||
}
|
||||
|
||||
return built
|
||||
return { ...added, ...beneath }
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ export const holding: Plugin = {
|
||||
})
|
||||
|
||||
on('command.run', { command: 'hold' }, async () => {
|
||||
await held?.telemetry.log({ event: 'held' })
|
||||
await held?.telemetry.log({ to: 'collector', event: 'held' })
|
||||
|
||||
return { text: 'served' }
|
||||
})
|
||||
|
||||
+7
-4
@@ -1,17 +1,20 @@
|
||||
import type { Plugin } from 'claude-code/testing'
|
||||
|
||||
/**
|
||||
* A plugin a person installed that hooks `telemetry.log` above the gate to
|
||||
* rename every row, and logs one of its own through `/meddle`.
|
||||
* A plugin a person installed that hooks `telemetry.log` above the gate for
|
||||
* the one stream it may, the collector's, to rename every record there, and
|
||||
* logs one of its own through `/meddle`.
|
||||
*/
|
||||
export const meddling: Plugin = {
|
||||
name: 'meddling',
|
||||
tier: 'user',
|
||||
register(on) {
|
||||
on('telemetry.log', (_$, e, next) => next({ ...e, event: 'renamed' }))
|
||||
on('telemetry.log', { to: 'collector' }, (_$, e, next) =>
|
||||
next({ ...e, event: 'renamed' }),
|
||||
)
|
||||
|
||||
on('command.run', { command: 'meddle' }, $ =>
|
||||
$.telemetry.log({ event: 'mine' }).then(
|
||||
$.telemetry.log({ to: 'collector', event: 'mine' }).then(
|
||||
() => ({ text: 'served' }),
|
||||
(error: unknown) => ({ text: String(error) }),
|
||||
),
|
||||
|
||||
+3
-3
@@ -17,7 +17,7 @@ export const reaching: Plugin = {
|
||||
const beneath = await next(e)
|
||||
|
||||
during = await beneath.telemetry
|
||||
.log({ event: 'during_create' })
|
||||
.log({ to: 'collector', event: 'during_create' })
|
||||
.then(() => 'served', String)
|
||||
|
||||
return beneath
|
||||
@@ -26,7 +26,7 @@ export const reaching: Plugin = {
|
||||
on('session.start', ($, e, next) => {
|
||||
$.clock.after(1, () => {
|
||||
void $.telemetry
|
||||
.log({ event: 'from_timer' })
|
||||
.log({ to: 'collector', event: 'from_timer' })
|
||||
.then(() => 'served', String)
|
||||
.then(outcome => $.ui.log(`timer: ${outcome}`, { to: 'debug' }))
|
||||
})
|
||||
@@ -36,7 +36,7 @@ export const reaching: Plugin = {
|
||||
|
||||
on('tool.call', { tool: 'Read' }, async ($, e, next) => {
|
||||
const outcome = await $.telemetry
|
||||
.log({ event: 'from_tool' })
|
||||
.log({ to: 'collector', event: 'from_tool' })
|
||||
.then(() => 'served', String)
|
||||
|
||||
await $.ui.log(`tool: ${outcome}`, { to: 'debug' })
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ export const replacing: Plugin = {
|
||||
}))
|
||||
|
||||
on('command.run', { command: 'replace' }, $ =>
|
||||
$.telemetry.log({ event: 'mine' }).then(
|
||||
$.telemetry.log({ to: 'collector', event: 'mine' }).then(
|
||||
() => ({ text: 'served' }),
|
||||
(error: unknown) => ({ text: String(error) }),
|
||||
),
|
||||
|
||||
+3
-2
@@ -2,12 +2,13 @@ import type { Plugin } from 'claude-code/testing'
|
||||
|
||||
/**
|
||||
* A plugin a person installed that answers `telemetry.log` above the gate
|
||||
* without going on beneath: the rows of everyone beneath it stop there.
|
||||
* without going on beneath, for the one stream it may hook, the
|
||||
* collector's: the records of everyone beneath it stop there.
|
||||
*/
|
||||
export const swallowing: Plugin = {
|
||||
name: 'swallowing',
|
||||
tier: 'user',
|
||||
register(on) {
|
||||
on('telemetry.log', () => ({ value: undefined }))
|
||||
on('telemetry.log', { to: 'collector' }, () => ({ value: undefined }))
|
||||
},
|
||||
}
|
||||
|
||||
+9
-6
@@ -1,18 +1,21 @@
|
||||
import type { Plugin } from 'claude-code/testing'
|
||||
|
||||
/**
|
||||
* A plugin a person installed whose `/visit <entry>` logs the entry through
|
||||
* `$.telemetry`, answering "queued", or why the row was refused.
|
||||
* A plugin a person installed whose `/visit <entry>` logs the entry's event
|
||||
* and properties through `$.telemetry`, naming the one stream it may, the
|
||||
* collector's; it answers "queued", or why the call was refused.
|
||||
*/
|
||||
export const visiting: Plugin = {
|
||||
name: 'visiting',
|
||||
tier: 'user',
|
||||
register(on) {
|
||||
on('command.run', { command: 'visit' }, ($, e) =>
|
||||
$.telemetry.log(JSON.parse(e.args)).then(
|
||||
on('command.run', { command: 'visit' }, ($, e) => {
|
||||
const { event, props } = JSON.parse(e.args)
|
||||
|
||||
return $.telemetry.log({ to: 'collector', event, props }).then(
|
||||
() => ({ text: 'queued' }),
|
||||
(error: unknown) => ({ text: String(error) }),
|
||||
),
|
||||
)
|
||||
)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ describe('gate', () => {
|
||||
)
|
||||
|
||||
test(
|
||||
'a hook above may rename a row a built-in sends, never send its own',
|
||||
"a hook above on the collector's stream renames no row, sends none",
|
||||
{ plugins: [Fixtures.recording, Fixtures.meddling] },
|
||||
async ($, on) => {
|
||||
mock.env(on, Fixtures.SENDING_ENV)
|
||||
@@ -77,13 +77,13 @@ describe('gate', () => {
|
||||
rows: Fixtures.rowsOf(session).map(Fixtures.eventNameOf),
|
||||
}).toEqual({
|
||||
own: `HooksError: meddling: $.telemetry.log: ${Hooks.REFUSED.deny}`,
|
||||
rows: ['tengu_plugin_renamed'],
|
||||
rows: ['tengu_plugin_survey_answered'],
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
test(
|
||||
'a hook above that answers without going on sends nothing at all',
|
||||
"a hook above that swallows the collector's stream stops no row",
|
||||
{ plugins: [Fixtures.recording, Fixtures.swallowing] },
|
||||
async ($, on) => {
|
||||
mock.env(on, Fixtures.SENDING_ENV)
|
||||
@@ -96,9 +96,14 @@ describe('gate', () => {
|
||||
|
||||
await session.clock.advance(Hooks.BATCH_WINDOW_MS)
|
||||
|
||||
expect({ answer, posts: session.posts }).toEqual({
|
||||
expect({
|
||||
answer,
|
||||
posts: session.posts.length,
|
||||
rows: Fixtures.rowsOf(session).map(Fixtures.eventNameOf),
|
||||
}).toEqual({
|
||||
answer: 'queued',
|
||||
posts: [],
|
||||
posts: 1,
|
||||
rows: ['tengu_plugin_survey_answered'],
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user