Merge remote-tracking branch 'origin/master' into codex/trim-ai-prose
# Conflicts: # packages/ui/acp/tests/stream-update.spec.ts
This commit is contained in:
commit
274db4966a
11 changed files with 244 additions and 71 deletions
14
.jscpd.json
Normal file
14
.jscpd.json
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"minTokens": 100,
|
||||||
|
"minLines": 10,
|
||||||
|
"mode": "mild",
|
||||||
|
"format": ["typescript"],
|
||||||
|
"pattern": "**/src/**/*.ts",
|
||||||
|
"ignorePattern": [
|
||||||
|
"(?s)/\\* jscpd:ignore-start \\*/.*?/\\* jscpd:ignore-end \\*/"
|
||||||
|
],
|
||||||
|
"reporters": ["console"],
|
||||||
|
"exitCode": 1,
|
||||||
|
"noColors": true,
|
||||||
|
"noTips": true
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import stylistic from '@stylistic/eslint-plugin'
|
import stylistic from '@stylistic/eslint-plugin'
|
||||||
|
import sonarjs from 'eslint-plugin-sonarjs'
|
||||||
import tseslint from 'typescript-eslint'
|
import tseslint from 'typescript-eslint'
|
||||||
|
|
||||||
// Strict type-aware correctness rules plus repository formatting. Tests/examples relax deliberate
|
// Strict type-aware correctness rules plus repository formatting. Tests/examples relax deliberate
|
||||||
|
|
@ -105,6 +106,17 @@ export default tseslint.config(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// --- file-local duplication (all owned TypeScript) ---------------------
|
||||||
|
{
|
||||||
|
files: ['packages/**/*.ts', 'examples/**/*.ts', 'scripts/**/*.ts'],
|
||||||
|
plugins: { sonarjs },
|
||||||
|
rules: {
|
||||||
|
// Cross-file clones are covered separately by jscpd.
|
||||||
|
'sonarjs/no-identical-functions': 'error',
|
||||||
|
'sonarjs/no-duplicated-branches': 'error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// --- formatting (everything we own) -------------------------------------
|
// --- formatting (everything we own) -------------------------------------
|
||||||
{
|
{
|
||||||
files: ['packages/**/*.ts', 'examples/**/*.ts', 'scripts/**/*.ts', 'eslint.config.mjs'],
|
files: ['packages/**/*.ts', 'examples/**/*.ts', 'scripts/**/*.ts', 'eslint.config.mjs'],
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
"typecheck": "tsc -b tsconfig.json",
|
"typecheck": "tsc -b tsconfig.json",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"lint:fix": "eslint . --fix",
|
"lint:fix": "eslint . --fix",
|
||||||
|
"duplication": "jscpd --config .jscpd.json packages",
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"test:coverage": "vitest run --coverage",
|
"test:coverage": "vitest run --coverage",
|
||||||
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
||||||
|
|
@ -81,7 +82,9 @@
|
||||||
"@types/node": "^22.20.0",
|
"@types/node": "^22.20.0",
|
||||||
"@vitest/coverage-v8": "^4.1.8",
|
"@vitest/coverage-v8": "^4.1.8",
|
||||||
"eslint": "^10.4.1",
|
"eslint": "^10.4.1",
|
||||||
|
"eslint-plugin-sonarjs": "^4.1.0",
|
||||||
"fast-check": "^4.8.0",
|
"fast-check": "^4.8.0",
|
||||||
|
"jscpd": "^5.0.12",
|
||||||
"jsdom": "29.1.1",
|
"jsdom": "29.1.1",
|
||||||
"knip": "^6.16.1",
|
"knip": "^6.16.1",
|
||||||
"lefthook": "^2.1.9",
|
"lefthook": "^2.1.9",
|
||||||
|
|
|
||||||
|
|
@ -98,16 +98,7 @@ async function callUntilText(
|
||||||
throw new Error(`${name} output did not include ${JSON.stringify(expected)}; last text was ${JSON.stringify(last !== undefined ? text(last) : '')}`)
|
throw new Error(`${name} output did not include ${JSON.stringify(expected)}; last text was ${JSON.stringify(last !== undefined ? text(last) : '')}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
class LossyReadBashExecutor extends BashExecutor {
|
abstract class TestBashExecutor extends BashExecutor {
|
||||||
private readonly task: BashTask = {
|
|
||||||
id: BashTaskId('bash-lossy'),
|
|
||||||
command: 'fake',
|
|
||||||
status: 'running',
|
|
||||||
exitCode: null,
|
|
||||||
signal: null,
|
|
||||||
done: Promise.resolve(),
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(request: BashExecRequest): BashExecSpec {
|
resolve(request: BashExecRequest): BashExecSpec {
|
||||||
return {
|
return {
|
||||||
command: request.command,
|
command: request.command,
|
||||||
|
|
@ -118,6 +109,17 @@ class LossyReadBashExecutor extends BashExecutor {
|
||||||
sandboxMode: request.sandboxMode,
|
sandboxMode: request.sandboxMode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LossyReadBashExecutor extends TestBashExecutor {
|
||||||
|
private readonly task: BashTask = {
|
||||||
|
id: BashTaskId('bash-lossy'),
|
||||||
|
command: 'fake',
|
||||||
|
status: 'running',
|
||||||
|
exitCode: null,
|
||||||
|
signal: null,
|
||||||
|
done: Promise.resolve(),
|
||||||
|
}
|
||||||
|
|
||||||
run(): Promise<BashRunResult> {
|
run(): Promise<BashRunResult> {
|
||||||
return Promise.reject(new Error('not used'))
|
return Promise.reject(new Error('not used'))
|
||||||
|
|
@ -1029,7 +1031,7 @@ describe('sandbox rendering', () => {
|
||||||
// it anyway: an executor that reports no sandboxMode (fields never
|
// it anyway: an executor that reports no sandboxMode (fields never
|
||||||
// advertised) whose task nonetheless carries denial facts must render
|
// advertised) whose task nonetheless carries denial facts must render
|
||||||
// the marker without suggesting a lever the schema does not offer.
|
// the marker without suggesting a lever the schema does not offer.
|
||||||
class FactsOnlyExecutor extends BashExecutor {
|
class FactsOnlyExecutor extends TestBashExecutor {
|
||||||
private readonly task: BashTask = {
|
private readonly task: BashTask = {
|
||||||
id: BashTaskId('bash-facts'),
|
id: BashTaskId('bash-facts'),
|
||||||
command: 'fake',
|
command: 'fake',
|
||||||
|
|
@ -1040,17 +1042,6 @@ describe('sandbox rendering', () => {
|
||||||
sandbox: { mode: 'read-only', denied: true },
|
sandbox: { mode: 'read-only', denied: true },
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(request: BashExecRequest): BashExecSpec {
|
|
||||||
return {
|
|
||||||
command: request.command,
|
|
||||||
workdir: request.workdir ?? process.cwd(),
|
|
||||||
timeoutMs: request.timeoutMs ?? 0,
|
|
||||||
...request.signal ? { signal: request.signal } : {},
|
|
||||||
owner: request.owner,
|
|
||||||
sandboxMode: request.sandboxMode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
run(): Promise<BashRunResult> { return Promise.reject(new Error('not used')) }
|
run(): Promise<BashRunResult> { return Promise.reject(new Error('not used')) }
|
||||||
start(): BashTask { return this.task }
|
start(): BashTask { return this.task }
|
||||||
get(id: string): BashTask | undefined { return id === this.task.id ? this.task : undefined }
|
get(id: string): BashTask | undefined { return id === this.task.id ? this.task : undefined }
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,9 @@ export function parseCodexConfig(raw: unknown): ParsedCodexConfig {
|
||||||
|
|
||||||
for (const event of CODEX_EVENTS) {
|
for (const event of CODEX_EVENTS) {
|
||||||
const rawGroups = hooksMap[event]
|
const rawGroups = hooksMap[event]
|
||||||
|
// Matcher-group parsing remains dialect-local because the supported hook
|
||||||
|
// shapes and skip reasons differ from Claude Code's.
|
||||||
|
/* jscpd:ignore-start */
|
||||||
if (!Array.isArray(rawGroups)) continue
|
if (!Array.isArray(rawGroups)) continue
|
||||||
const groups: MatcherGroup[] = []
|
const groups: MatcherGroup[] = []
|
||||||
for (const rawGroup of rawGroups) {
|
for (const rawGroup of rawGroups) {
|
||||||
|
|
@ -57,6 +60,7 @@ export function parseCodexConfig(raw: unknown): ParsedCodexConfig {
|
||||||
if (!hook) continue
|
if (!hook) continue
|
||||||
const type = typeof hook.type === 'string' ? hook.type : 'command'
|
const type = typeof hook.type === 'string' ? hook.type : 'command'
|
||||||
if (type !== 'command') { skipped.push({ event, reason: `unsupported "${type}" hook` }); continue }
|
if (type !== 'command') { skipped.push({ event, reason: `unsupported "${type}" hook` }); continue }
|
||||||
|
/* jscpd:ignore-end */
|
||||||
if (hook.async === true) { skipped.push({ event, reason: 'async hook' }); continue }
|
if (hook.async === true) { skipped.push({ event, reason: 'async hook' }); continue }
|
||||||
if (typeof hook.command !== 'string') continue
|
if (typeof hook.command !== 'string') continue
|
||||||
// Codex accepts `timeout` or the `timeoutSec` alias.
|
// Codex accepts `timeout` or the `timeoutSec` alias.
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,9 @@ export function apply(ctx: Context, config: Config): void {
|
||||||
output.additionalContext = output.stdout
|
output.additionalContext = output.stdout
|
||||||
}
|
}
|
||||||
outputs.push(output)
|
outputs.push(output)
|
||||||
|
// Execution and decision mapping remain in each bridge so dialect
|
||||||
|
// differences stay explicit at their owning seam.
|
||||||
|
/* jscpd:ignore-start */
|
||||||
if (output.systemMessage !== undefined) {
|
if (output.systemMessage !== undefined) {
|
||||||
ctx.logger.warn(`hooks-codex: ${point} hook emitted a systemMessage, which is not yet surfaced (ignored)`)
|
ctx.logger.warn(`hooks-codex: ${point} hook emitted a systemMessage, which is not yet surfaced (ignored)`)
|
||||||
}
|
}
|
||||||
|
|
@ -171,12 +174,14 @@ export function apply(ctx: Context, config: Config): void {
|
||||||
if (context) agent.inject(context.content, { source: context.source })
|
if (context) agent.inject(context.content, { source: context.source })
|
||||||
})
|
})
|
||||||
.catch((error: unknown) => { ctx.logger.warn(`hooks-codex: SessionStart hook failed: ${String(error)}`) }))
|
.catch((error: unknown) => { ctx.logger.warn(`hooks-codex: SessionStart hook failed: ${String(error)}`) }))
|
||||||
|
/* jscpd:ignore-end */
|
||||||
})
|
})
|
||||||
|
|
||||||
// UserPromptSubmit → PromptDecision. Codex can only BLOCK (no allow/ask).
|
// UserPromptSubmit → PromptDecision. Codex can only BLOCK (no allow/ask).
|
||||||
ctx.on('agent/prompt-submit', async (agent, content, _source, next): Promise<PromptDecision> => {
|
ctx.on('agent/prompt-submit', async (agent, content, _source, next): Promise<PromptDecision> => {
|
||||||
const turn = lastTurn(agent)
|
const turn = lastTurn(agent)
|
||||||
const merged = await runPoint('UserPromptSubmit', '', { ...turnBase(agent, 'UserPromptSubmit', model), prompt: blocksToText(content) }, { agent, turn, plainStdoutAsContext: true })
|
const merged = await runPoint('UserPromptSubmit', '', { ...turnBase(agent, 'UserPromptSubmit', model), prompt: blocksToText(content) }, { agent, turn, plainStdoutAsContext: true })
|
||||||
|
/* jscpd:ignore-start */
|
||||||
if (merged.decision === 'deny') return { kind: 'block', reason: merged.reason ?? 'blocked by UserPromptSubmit hook' }
|
if (merged.decision === 'deny') return { kind: 'block', reason: merged.reason ?? 'blocked by UserPromptSubmit hook' }
|
||||||
// Context alone is not a veto: DELEGATE so a later prompt-submit listener can
|
// Context alone is not a veto: DELEGATE so a later prompt-submit listener can
|
||||||
// still block/rewrite, then fold our context onto its decision.
|
// still block/rewrite, then fold our context onto its decision.
|
||||||
|
|
@ -194,6 +199,7 @@ export function apply(ctx: Context, config: Config): void {
|
||||||
ctx.on('tools/pre-execute', async (exec, next): Promise<PreToolDecision> => {
|
ctx.on('tools/pre-execute', async (exec, next): Promise<PreToolDecision> => {
|
||||||
const turn = lastTurn(exec.agent)
|
const turn = lastTurn(exec.agent)
|
||||||
const merged = await runPoint('PreToolUse', exec.name, preToolPayload(exec, model), { ...exec.agent ? { agent: exec.agent } : {}, turn, ...exec.signal ? { signal: exec.signal } : {} })
|
const merged = await runPoint('PreToolUse', exec.name, preToolPayload(exec, model), { ...exec.agent ? { agent: exec.agent } : {}, turn, ...exec.signal ? { signal: exec.signal } : {} })
|
||||||
|
/* jscpd:ignore-end */
|
||||||
if (merged.decision === 'deny') return { kind: 'deny', reason: merged.reason ?? 'blocked by PreToolUse hook' }
|
if (merged.decision === 'deny') return { kind: 'deny', reason: merged.reason ?? 'blocked by PreToolUse hook' }
|
||||||
return next()
|
return next()
|
||||||
})
|
})
|
||||||
|
|
@ -201,6 +207,7 @@ export function apply(ctx: Context, config: Config): void {
|
||||||
// PostToolUse → PostToolDecision (block with feedback, or attach context).
|
// PostToolUse → PostToolDecision (block with feedback, or attach context).
|
||||||
ctx.on('tools/post-execute', async (exec, result, next): Promise<PostToolDecision> => {
|
ctx.on('tools/post-execute', async (exec, result, next): Promise<PostToolDecision> => {
|
||||||
const turn = lastTurn(exec.agent)
|
const turn = lastTurn(exec.agent)
|
||||||
|
/* jscpd:ignore-start */
|
||||||
const merged = await runPoint('PostToolUse', exec.name, postToolPayload(exec, result, model), { ...exec.agent ? { agent: exec.agent } : {}, turn, ...exec.signal ? { signal: exec.signal } : {} })
|
const merged = await runPoint('PostToolUse', exec.name, postToolPayload(exec, result, model), { ...exec.agent ? { agent: exec.agent } : {}, turn, ...exec.signal ? { signal: exec.signal } : {} })
|
||||||
const context = contextFrom(merged)
|
const context = contextFrom(merged)
|
||||||
if (merged.decision === 'deny') {
|
if (merged.decision === 'deny') {
|
||||||
|
|
@ -226,6 +233,7 @@ export function apply(ctx: Context, config: Config): void {
|
||||||
// loop-guard (stop_hook_active + a max-consecutive cap) is deferred.
|
// loop-guard (stop_hook_active + a max-consecutive cap) is deferred.
|
||||||
ctx.on('agent/turn-continuation', async (agent, turn, _default, next): Promise<ContinuationDecision> => {
|
ctx.on('agent/turn-continuation', async (agent, turn, _default, next): Promise<ContinuationDecision> => {
|
||||||
const merged = await runPoint('Stop', '', { ...turnBase(agent, 'Stop', model), stop_hook_active: false, last_assistant_message: null }, { agent, turn })
|
const merged = await runPoint('Stop', '', { ...turnBase(agent, 'Stop', model), stop_hook_active: false, last_assistant_message: null }, { agent, turn })
|
||||||
|
/* jscpd:ignore-end */
|
||||||
if (merged.decision === 'deny') {
|
if (merged.decision === 'deny') {
|
||||||
// A blocking Stop hook forces continuation; a block with no reason (exit 2,
|
// A blocking Stop hook forces continuation; a block with no reason (exit 2,
|
||||||
// empty stderr) still forces it — fall back to a generic steering line
|
// empty stderr) still forces it — fall back to a generic steering line
|
||||||
|
|
@ -240,6 +248,9 @@ export function apply(ctx: Context, config: Config): void {
|
||||||
// --- Codex DIALECT payloads: snake_case, model on every event, turn_id on
|
// --- Codex DIALECT payloads: snake_case, model on every event, turn_id on
|
||||||
// turn-scoped events. ---
|
// turn-scoped events. ---
|
||||||
|
|
||||||
|
// These small payload helpers intentionally remain next to the dialect shape;
|
||||||
|
// sharing them would pull bridge-only agent/LLM dependencies into hook-protocol.
|
||||||
|
/* jscpd:ignore-start */
|
||||||
function lastTurn(agent: Agent | undefined): number {
|
function lastTurn(agent: Agent | undefined): number {
|
||||||
if (!agent) return 0
|
if (!agent) return 0
|
||||||
const last = [...agent.session.events].findLast(e => e.type === 'turn/start')
|
const last = [...agent.session.events].findLast(e => e.type === 'turn/start')
|
||||||
|
|
@ -252,6 +263,7 @@ function lastTurn(agent: Agent | undefined): number {
|
||||||
function blocksToText(content: ContentBlock[]): string {
|
function blocksToText(content: ContentBlock[]): string {
|
||||||
return content.filter((b): b is Extract<ContentBlock, { type: 'text' }> => b.type === 'text').map(b => b.text).join('')
|
return content.filter((b): b is Extract<ContentBlock, { type: 'text' }> => b.type === 'text').map(b => b.text).join('')
|
||||||
}
|
}
|
||||||
|
/* jscpd:ignore-end */
|
||||||
|
|
||||||
/** Base fields on every Codex payload (no turn_id). */
|
/** Base fields on every Codex payload (no turn_id). */
|
||||||
function base(agent: Agent | undefined, event: string, model: string): Record<string, unknown> {
|
function base(agent: Agent | undefined, event: string, model: string): Record<string, unknown> {
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,9 @@ export class SessionPersistenceJsonl extends SessionPersistence implements Persi
|
||||||
this.coordinator = new PersistenceCoordinator<number>(this.ctx, this)
|
this.coordinator = new PersistenceCoordinator<number>(this.ctx, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Each backend keeps the typed service surface beside its storage hooks;
|
||||||
|
// extracting these trivial forwards would add an inheritance seam.
|
||||||
|
/* jscpd:ignore-start */
|
||||||
// --- SessionPersistence service surface (delegated to the coordinator) ---
|
// --- SessionPersistence service surface (delegated to the coordinator) ---
|
||||||
|
|
||||||
create(meta: SessionHeader): Promise<void> {
|
create(meta: SessionHeader): Promise<void> {
|
||||||
|
|
@ -97,6 +100,7 @@ export class SessionPersistenceJsonl extends SessionPersistence implements Persi
|
||||||
get inits(): Map<Session, Promise<void>> {
|
get inits(): Map<Session, Promise<void>> {
|
||||||
return this.coordinator.inits
|
return this.coordinator.inits
|
||||||
}
|
}
|
||||||
|
/* jscpd:ignore-end */
|
||||||
|
|
||||||
// --- PersistenceBackend hooks (the file-bytes storage primitives) ---
|
// --- PersistenceBackend hooks (the file-bytes storage primitives) ---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,16 @@ function chunkEvent(seq: number, turn: number, step: number, chunk: StreamChunk)
|
||||||
let dir: string
|
let dir: string
|
||||||
let file: string
|
let file: string
|
||||||
|
|
||||||
|
/** Write a session log file and return its path. */
|
||||||
|
function writeSession(filename: string, header: { id: string; createdAt: number }, calls: StreamChunk[][]): string {
|
||||||
|
let seq = 1
|
||||||
|
const events: SessionEvent[] = []
|
||||||
|
calls.forEach((chunks, step) => { for (const c of chunks) events.push(chunkEvent(seq++, 1, step + 1, c)) })
|
||||||
|
const path = join(dir, filename)
|
||||||
|
writeFileSync(path, sessionJsonl(events, header), 'utf8')
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
dir = mkdtempSync(join(tmpdir(), 'llm-replay-spec-'))
|
dir = mkdtempSync(join(tmpdir(), 'llm-replay-spec-'))
|
||||||
file = join(dir, 'session.jsonl')
|
file = join(dir, 'session.jsonl')
|
||||||
|
|
@ -391,16 +401,6 @@ describe('parseSessionHeader', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('loadSessionScripts', () => {
|
describe('loadSessionScripts', () => {
|
||||||
/** Write a session log file and return its path. */
|
|
||||||
function writeSession(filename: string, header: { id: string; createdAt: number }, calls: StreamChunk[][]): string {
|
|
||||||
let seq = 1
|
|
||||||
const events: SessionEvent[] = []
|
|
||||||
calls.forEach((chunks, step) => { for (const c of chunks) events.push(chunkEvent(seq++, 1, step + 1, c)) })
|
|
||||||
const path = join(dir, filename)
|
|
||||||
writeFileSync(path, sessionJsonl(events, header), 'utf8')
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
it('returns one primary script for a single-session scenario', () => {
|
it('returns one primary script for a single-session scenario', () => {
|
||||||
const f = writeSession('session.jsonl', { id: 'p', createdAt: 100 }, [TEXT_CHUNKS])
|
const f = writeSession('session.jsonl', { id: 'p', createdAt: 100 }, [TEXT_CHUNKS])
|
||||||
const scripts: SessionScript[] = loadSessionScripts({ file: f })
|
const scripts: SessionScript[] = loadSessionScripts({ file: f })
|
||||||
|
|
@ -502,16 +502,6 @@ describe('installLlmReplay (per-session keying)', () => {
|
||||||
{ type: 'finish', reason: { kind: 'stop' } },
|
{ type: 'finish', reason: { kind: 'stop' } },
|
||||||
]
|
]
|
||||||
|
|
||||||
/** Write a session log file and return its path. */
|
|
||||||
function writeSession(filename: string, header: { id: string; createdAt: number }, calls: StreamChunk[][]): string {
|
|
||||||
let seq = 1
|
|
||||||
const events: SessionEvent[] = []
|
|
||||||
calls.forEach((chunks, step) => { for (const c of chunks) events.push(chunkEvent(seq++, 1, step + 1, c)) })
|
|
||||||
const path = join(dir, filename)
|
|
||||||
writeFileSync(path, sessionJsonl(events, header), 'utf8')
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
const live = (id: string): GenerateOptions =>
|
const live = (id: string): GenerateOptions =>
|
||||||
({ model: 'm', messages: [], sessionId: id as NonNullable<GenerateOptions['sessionId']> })
|
({ model: 'm', messages: [], sessionId: id as NonNullable<GenerateOptions['sessionId']> })
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,21 @@ function registryOf(...tools: ToolDefinition[]): Pick<ToolRegistryType, 'get'> {
|
||||||
return { get: name => map.get(name) }
|
return { get: name => map.get(name) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updatesWith(presenter: ToolPresenter, ...events: SessionEvent[]): SessionNotification['update'][] {
|
||||||
|
const out: SessionNotification['update'][] = []
|
||||||
|
for (const event of events) streamSessionEventUpdate(SessionId('s1'), event, n => out.push(n.update), presenter)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fsCtx(): Promise<Context> {
|
||||||
|
const ctx = new Context()
|
||||||
|
await ctx.plugin(SystemPrompt)
|
||||||
|
await ctx.plugin(ToolRegistry)
|
||||||
|
await ctx.plugin(FsLocal)
|
||||||
|
await ctx.plugin(ToolFs)
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
function evt<T extends SessionEvent['type']>(type: T, data: Extract<SessionEvent, { type: T }>['data']): SessionEvent {
|
function evt<T extends SessionEvent['type']>(type: T, data: Extract<SessionEvent, { type: T }>['data']): SessionEvent {
|
||||||
return { type, seq: 0, time: 0, data } as SessionEvent
|
return { type, seq: 0, time: 0, data } as SessionEvent
|
||||||
}
|
}
|
||||||
|
|
@ -181,12 +196,6 @@ describe('ToolPresenter (tool-owned presentation via the tool registry)', () =>
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatesWith(presenter: ToolPresenter, ...events: SessionEvent[]): SessionNotification['update'][] {
|
|
||||||
const out: SessionNotification['update'][] = []
|
|
||||||
for (const event of events) streamSessionEventUpdate(SessionId('s1'), event, n => out.push(n.update), presenter)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
it('tool/call uses the tool: description→title, command→rawInput, tool kind', () => {
|
it('tool/call uses the tool: description→title, command→rawInput, tool kind', () => {
|
||||||
const presenter = new ToolPresenter(registryOf(bashLike))
|
const presenter = new ToolPresenter(registryOf(bashLike))
|
||||||
const [update] = updatesWith(presenter, evt('tool/call', {
|
const [update] = updatesWith(presenter, evt('tool/call', {
|
||||||
|
|
@ -624,21 +633,6 @@ describe('result-time diff card (REAL fs edit tool → tool_call_update diff blo
|
||||||
// call-time snippet, then the tool/result carries the tool's computed applied-hunk `meta`,
|
// call-time snippet, then the tool/result carries the tool's computed applied-hunk `meta`,
|
||||||
// which presentResult narrows into a `diff` result card the bridge forwards as `{ type:
|
// which presentResult narrows into a `diff` result card the bridge forwards as `{ type:
|
||||||
// 'diff' }` content blocks. The real tool is required because its result metadata is the contract.
|
// 'diff' }` content blocks. The real tool is required because its result metadata is the contract.
|
||||||
async function fsCtx(): Promise<Context> {
|
|
||||||
const ctx = new Context()
|
|
||||||
await ctx.plugin(SystemPrompt)
|
|
||||||
await ctx.plugin(ToolRegistry)
|
|
||||||
await ctx.plugin(FsLocal)
|
|
||||||
await ctx.plugin(ToolFs)
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatesWith(presenter: ToolPresenter, ...events: SessionEvent[]): SessionNotification['update'][] {
|
|
||||||
const out: SessionNotification['update'][] = []
|
|
||||||
for (const event of events) streamSessionEventUpdate(SessionId('s1'), event, n => out.push(n.update), presenter)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
it('forwards the applied-hunk meta onto the wire as tool_call_update diff content', async () => {
|
it('forwards the applied-hunk meta onto the wire as tool_call_update diff content', async () => {
|
||||||
const ctx = await fsCtx()
|
const ctx = await fsCtx()
|
||||||
const presenter = new ToolPresenter(ctx.tools)
|
const presenter = new ToolPresenter(ctx.tools)
|
||||||
|
|
@ -730,14 +724,6 @@ describe('relative-path display titles (bridge relativizes the title against the
|
||||||
// The bridge relativizes a file card's TITLE against the session workspace cwd (mirroring the
|
// The bridge relativizes a file card's TITLE against the session workspace cwd (mirroring the
|
||||||
// reference adapter's `toDisplayPath`), while leaving location/diff paths raw. Use real fs tools
|
// reference adapter's `toDisplayPath`), while leaving location/diff paths raw. Use real fs tools
|
||||||
// and the absolute paths an editor supplies; presentation itself is args-only and lacks cwd.
|
// and the absolute paths an editor supplies; presentation itself is args-only and lacks cwd.
|
||||||
async function fsCtx(): Promise<Context> {
|
|
||||||
const ctx = new Context()
|
|
||||||
await ctx.plugin(SystemPrompt)
|
|
||||||
await ctx.plugin(ToolRegistry)
|
|
||||||
await ctx.plugin(FsLocal)
|
|
||||||
await ctx.plugin(ToolFs)
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
function callUpdate(ctx: Context, sessionCwd: string | undefined, name: string, args: unknown): SessionNotification['update'] {
|
function callUpdate(ctx: Context, sessionCwd: string | undefined, name: string, args: unknown): SessionNotification['update'] {
|
||||||
const presenter = new ToolPresenter(ctx.tools)
|
const presenter = new ToolPresenter(ctx.tools)
|
||||||
const out: SessionNotification['update'][] = []
|
const out: SessionNotification['update'][] = []
|
||||||
|
|
|
||||||
154
pnpm-lock.yaml
generated
154
pnpm-lock.yaml
generated
|
|
@ -29,9 +29,15 @@ importers:
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^10.4.1
|
specifier: ^10.4.1
|
||||||
version: 10.5.0(jiti@2.7.0)
|
version: 10.5.0(jiti@2.7.0)
|
||||||
|
eslint-plugin-sonarjs:
|
||||||
|
specifier: ^4.1.0
|
||||||
|
version: 4.1.0(eslint@10.5.0(jiti@2.7.0))
|
||||||
fast-check:
|
fast-check:
|
||||||
specifier: ^4.8.0
|
specifier: ^4.8.0
|
||||||
version: 4.8.0
|
version: 4.8.0
|
||||||
|
jscpd:
|
||||||
|
specifier: ^5.0.12
|
||||||
|
version: 5.0.12
|
||||||
jsdom:
|
jsdom:
|
||||||
specifier: 29.1.1
|
specifier: 29.1.1
|
||||||
version: 29.1.1
|
version: 29.1.1
|
||||||
|
|
@ -3050,6 +3056,14 @@ packages:
|
||||||
buffer-equal-constant-time@1.0.1:
|
buffer-equal-constant-time@1.0.1:
|
||||||
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
|
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
|
||||||
|
|
||||||
|
builtin-modules@3.3.0:
|
||||||
|
resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
bytes@3.1.2:
|
||||||
|
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
cac@7.0.0:
|
cac@7.0.0:
|
||||||
resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==}
|
resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==}
|
||||||
engines: {node: '>=20.19.0'}
|
engines: {node: '>=20.19.0'}
|
||||||
|
|
@ -3356,6 +3370,11 @@ packages:
|
||||||
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
|
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
eslint-plugin-sonarjs@4.1.0:
|
||||||
|
resolution: {integrity: sha512-rh+FlVz0yfd2RNIb6WqSkuGh0addX/Qi5scwQ5FphXDFrM6fZKcxP1+attJ78yUKcyYfiu6MTaISPpAFPzqRJw==}
|
||||||
|
peerDependencies:
|
||||||
|
eslint: ^8.0.0 || ^9.0.0 || ^10.0.0
|
||||||
|
|
||||||
eslint-scope@9.1.2:
|
eslint-scope@9.1.2:
|
||||||
resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==}
|
resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==}
|
||||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
||||||
|
|
@ -3481,6 +3500,9 @@ packages:
|
||||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
|
functional-red-black-tree@1.0.1:
|
||||||
|
resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==}
|
||||||
|
|
||||||
gaxios@7.1.5:
|
gaxios@7.1.5:
|
||||||
resolution: {integrity: sha512-5FZy72Rh8LhtjmvDrKkI+lVhrsQrVKVsItxMoDm5mNQE+xR0WVIIs+jzPSJgBvKVsLi24fZhXJIsNI0bihDzFg==}
|
resolution: {integrity: sha512-5FZy72Rh8LhtjmvDrKkI+lVhrsQrVKVsItxMoDm5mNQE+xR0WVIIs+jzPSJgBvKVsLi24fZhXJIsNI0bihDzFg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
@ -3500,6 +3522,10 @@ packages:
|
||||||
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
|
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
|
|
||||||
|
globals@17.7.0:
|
||||||
|
resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
globrex@0.1.2:
|
globrex@0.1.2:
|
||||||
resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
|
resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
|
||||||
|
|
||||||
|
|
@ -3606,6 +3632,44 @@ packages:
|
||||||
resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==}
|
resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
jscpd-darwin-arm64@5.0.12:
|
||||||
|
resolution: {integrity: sha512-CQCDYhQY9yOGGeauzkRGYW/QtXurPCjfDkEhUoM/M5UdmpzxfG3i9mnNsaheJ0RdFRf73mL7JaLVRP8U2l5/kA==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
jscpd-darwin-x64@5.0.12:
|
||||||
|
resolution: {integrity: sha512-7XNCDfChP9fPkIWLepd0zGnTov7/5mR7rJ5Utc1MdWFdgkN//qz6FAJ1FhQA0E/qjFedy9XTucfUCgLybSBsjw==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
jscpd-linux-arm64-gnu@5.0.12:
|
||||||
|
resolution: {integrity: sha512-i/usHD0/8twzb2Qo4vFWcnuAD4IDLS6K/mTXwYy1CiJZDw4gmjfH45jtjdKUyoutTAiNs+ZWZgx1gIRljUbFuA==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
jscpd-linux-x64-gnu@5.0.12:
|
||||||
|
resolution: {integrity: sha512-TqaeSTaGawcqyXSrQvYJFtLRn4aQeHgphGGsq2ZtVAg+lPeuMmh81c3bl0yi2dL3gDX1RGBQKBVul1XQknOuoA==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
jscpd-linux-x64-musl@5.0.12:
|
||||||
|
resolution: {integrity: sha512-WelugY1/rdoo0Y0sqJ2XQOIvyIZTfRe+vP3MJe4XvEUwPF0v+9SQoS34FnfblRhsddVixyNkgl7x/sHid9UMJw==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
jscpd-windows-x64-msvc@5.0.12:
|
||||||
|
resolution: {integrity: sha512-01x1klKjvfzI6Of5tI9u72x7TIQZQLLG6kMdsEPJKl/BXbskdknrfCepeTXRBbdFPKs25jfcd0klZ4OdDvww5w==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
jscpd@5.0.12:
|
||||||
|
resolution: {integrity: sha512-87dC+akj2mCywlt8p3xnRlDg0B55u4GDbfE9cuwOOeIxbEuWuIoNqGoYi65A0516aJ4EzAjGwBj1Qb/Ahc8WAQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
jsdom@29.1.1:
|
jsdom@29.1.1:
|
||||||
resolution: {integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==}
|
resolution: {integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==}
|
||||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0}
|
engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0}
|
||||||
|
|
@ -3636,6 +3700,10 @@ packages:
|
||||||
json-stable-stringify-without-jsonify@1.0.1:
|
json-stable-stringify-without-jsonify@1.0.1:
|
||||||
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
||||||
|
|
||||||
|
jsx-ast-utils-x@0.1.0:
|
||||||
|
resolution: {integrity: sha512-eQQBjBnsVtGacsG9uJNB8qOr3yA8rga4wAaGG1qRcBzSIvfhERLrWxMAM1hp5fcS6Abo8M4+bUBTekYR0qTPQw==}
|
||||||
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
jwa@2.0.1:
|
jwa@2.0.1:
|
||||||
resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==}
|
resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==}
|
||||||
|
|
||||||
|
|
@ -3802,6 +3870,9 @@ packages:
|
||||||
lodash-es@4.18.1:
|
lodash-es@4.18.1:
|
||||||
resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==}
|
resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==}
|
||||||
|
|
||||||
|
lodash.merge@4.6.2:
|
||||||
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
|
|
||||||
long@5.3.2:
|
long@5.3.2:
|
||||||
resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==}
|
resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==}
|
||||||
|
|
||||||
|
|
@ -4107,6 +4178,14 @@ packages:
|
||||||
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
|
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
|
||||||
engines: {node: '>= 14.18.0'}
|
engines: {node: '>= 14.18.0'}
|
||||||
|
|
||||||
|
refa@0.12.1:
|
||||||
|
resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==}
|
||||||
|
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
|
||||||
|
|
||||||
|
regexp-ast-analysis@0.7.1:
|
||||||
|
resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==}
|
||||||
|
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
|
||||||
|
|
||||||
require-from-string@2.0.2:
|
require-from-string@2.0.2:
|
||||||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
@ -4173,6 +4252,10 @@ packages:
|
||||||
schemastery@3.18.0:
|
schemastery@3.18.0:
|
||||||
resolution: {integrity: sha512-Jw2uxjoyyqc/yeurmChUEc/jbi8GsrdXV/KmqRUDZXJAXAmrJiPsz8vKa17l/VckyzljHZ9oGaul443CQiXxtA==}
|
resolution: {integrity: sha512-Jw2uxjoyyqc/yeurmChUEc/jbi8GsrdXV/KmqRUDZXJAXAmrJiPsz8vKa17l/VckyzljHZ9oGaul443CQiXxtA==}
|
||||||
|
|
||||||
|
scslre@0.3.0:
|
||||||
|
resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==}
|
||||||
|
engines: {node: ^14.0.0 || >=16.0.0}
|
||||||
|
|
||||||
semver@7.8.4:
|
semver@7.8.4:
|
||||||
resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==}
|
resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -5855,6 +5938,10 @@ snapshots:
|
||||||
|
|
||||||
buffer-equal-constant-time@1.0.1: {}
|
buffer-equal-constant-time@1.0.1: {}
|
||||||
|
|
||||||
|
builtin-modules@3.3.0: {}
|
||||||
|
|
||||||
|
bytes@3.1.2: {}
|
||||||
|
|
||||||
cac@7.0.0: {}
|
cac@7.0.0: {}
|
||||||
|
|
||||||
ccount@2.0.1: {}
|
ccount@2.0.1: {}
|
||||||
|
|
@ -6186,6 +6273,23 @@ snapshots:
|
||||||
|
|
||||||
escape-string-regexp@5.0.0: {}
|
escape-string-regexp@5.0.0: {}
|
||||||
|
|
||||||
|
eslint-plugin-sonarjs@4.1.0(eslint@10.5.0(jiti@2.7.0)):
|
||||||
|
dependencies:
|
||||||
|
'@eslint-community/regexpp': 4.12.2
|
||||||
|
builtin-modules: 3.3.0
|
||||||
|
bytes: 3.1.2
|
||||||
|
eslint: 10.5.0(jiti@2.7.0)
|
||||||
|
functional-red-black-tree: 1.0.1
|
||||||
|
globals: 17.7.0
|
||||||
|
jsx-ast-utils-x: 0.1.0
|
||||||
|
lodash.merge: 4.6.2
|
||||||
|
minimatch: 10.2.5
|
||||||
|
scslre: 0.3.0
|
||||||
|
semver: 7.8.4
|
||||||
|
ts-api-utils: 2.5.0(typescript@6.0.3)
|
||||||
|
typescript: 6.0.3
|
||||||
|
yaml: 2.9.0
|
||||||
|
|
||||||
eslint-scope@9.1.2:
|
eslint-scope@9.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/esrecurse': 4.3.1
|
'@types/esrecurse': 4.3.1
|
||||||
|
|
@ -6330,6 +6434,8 @@ snapshots:
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
functional-red-black-tree@1.0.1: {}
|
||||||
|
|
||||||
gaxios@7.1.5:
|
gaxios@7.1.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
extend: 3.0.2
|
extend: 3.0.2
|
||||||
|
|
@ -6358,6 +6464,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
|
|
||||||
|
globals@17.7.0: {}
|
||||||
|
|
||||||
globrex@0.1.2: {}
|
globrex@0.1.2: {}
|
||||||
|
|
||||||
google-auth-library@10.7.0:
|
google-auth-library@10.7.0:
|
||||||
|
|
@ -6452,6 +6560,33 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
argparse: 2.0.1
|
argparse: 2.0.1
|
||||||
|
|
||||||
|
jscpd-darwin-arm64@5.0.12:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
jscpd-darwin-x64@5.0.12:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
jscpd-linux-arm64-gnu@5.0.12:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
jscpd-linux-x64-gnu@5.0.12:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
jscpd-linux-x64-musl@5.0.12:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
jscpd-windows-x64-msvc@5.0.12:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
jscpd@5.0.12:
|
||||||
|
optionalDependencies:
|
||||||
|
jscpd-darwin-arm64: 5.0.12
|
||||||
|
jscpd-darwin-x64: 5.0.12
|
||||||
|
jscpd-linux-arm64-gnu: 5.0.12
|
||||||
|
jscpd-linux-x64-gnu: 5.0.12
|
||||||
|
jscpd-linux-x64-musl: 5.0.12
|
||||||
|
jscpd-windows-x64-msvc: 5.0.12
|
||||||
|
|
||||||
jsdom@29.1.1:
|
jsdom@29.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@asamuzakjp/css-color': 5.1.11
|
'@asamuzakjp/css-color': 5.1.11
|
||||||
|
|
@ -6495,6 +6630,8 @@ snapshots:
|
||||||
|
|
||||||
json-stable-stringify-without-jsonify@1.0.1: {}
|
json-stable-stringify-without-jsonify@1.0.1: {}
|
||||||
|
|
||||||
|
jsx-ast-utils-x@0.1.0: {}
|
||||||
|
|
||||||
jwa@2.0.1:
|
jwa@2.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
buffer-equal-constant-time: 1.0.1
|
buffer-equal-constant-time: 1.0.1
|
||||||
|
|
@ -6639,6 +6776,8 @@ snapshots:
|
||||||
|
|
||||||
lodash-es@4.18.1: {}
|
lodash-es@4.18.1: {}
|
||||||
|
|
||||||
|
lodash.merge@4.6.2: {}
|
||||||
|
|
||||||
long@5.3.2: {}
|
long@5.3.2: {}
|
||||||
|
|
||||||
longest-streak@3.1.0: {}
|
longest-streak@3.1.0: {}
|
||||||
|
|
@ -7155,6 +7294,15 @@ snapshots:
|
||||||
|
|
||||||
readdirp@4.1.2: {}
|
readdirp@4.1.2: {}
|
||||||
|
|
||||||
|
refa@0.12.1:
|
||||||
|
dependencies:
|
||||||
|
'@eslint-community/regexpp': 4.12.2
|
||||||
|
|
||||||
|
regexp-ast-analysis@0.7.1:
|
||||||
|
dependencies:
|
||||||
|
'@eslint-community/regexpp': 4.12.2
|
||||||
|
refa: 0.12.1
|
||||||
|
|
||||||
require-from-string@2.0.2: {}
|
require-from-string@2.0.2: {}
|
||||||
|
|
||||||
resolve-pkg-maps@1.0.0: {}
|
resolve-pkg-maps@1.0.0: {}
|
||||||
|
|
@ -7247,6 +7395,12 @@ snapshots:
|
||||||
'@standard-schema/spec': 1.1.0
|
'@standard-schema/spec': 1.1.0
|
||||||
cosmokit: 1.8.1
|
cosmokit: 1.8.1
|
||||||
|
|
||||||
|
scslre@0.3.0:
|
||||||
|
dependencies:
|
||||||
|
'@eslint-community/regexpp': 4.12.2
|
||||||
|
refa: 0.12.1
|
||||||
|
regexp-ast-analysis: 0.7.1
|
||||||
|
|
||||||
semver@7.8.4: {}
|
semver@7.8.4: {}
|
||||||
|
|
||||||
shebang-command@2.0.0:
|
shebang-command@2.0.0:
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@ function gatesForMode(selected: Mode): Gate[] {
|
||||||
case 'ci-lint':
|
case 'ci-lint':
|
||||||
return [
|
return [
|
||||||
lintGate(),
|
lintGate(),
|
||||||
|
pnpmScript('duplication', 'duplication'),
|
||||||
]
|
]
|
||||||
case 'ci-coverage':
|
case 'ci-coverage':
|
||||||
return [
|
return [
|
||||||
|
|
@ -152,6 +153,7 @@ function gatesForMode(selected: Mode): Gate[] {
|
||||||
case 'pre-push':
|
case 'pre-push':
|
||||||
return [
|
return [
|
||||||
pnpmScript('test', 'test'),
|
pnpmScript('test', 'test'),
|
||||||
|
pnpmScript('duplication', 'duplication'),
|
||||||
pnpmScript('snapshot', 'test:snapshot'),
|
pnpmScript('snapshot', 'test:snapshot'),
|
||||||
pnpmScript('build', 'build'),
|
pnpmScript('build', 'build'),
|
||||||
...hygieneLeafGates({ artifactNeeds: ['build'] }),
|
...hygieneLeafGates({ artifactNeeds: ['build'] }),
|
||||||
|
|
@ -166,6 +168,7 @@ function ciPrimaryGates(): Gate[] {
|
||||||
pnpmScript('constraints', 'constraints'),
|
pnpmScript('constraints', 'constraints'),
|
||||||
pnpmScript('typecheck', 'typecheck'),
|
pnpmScript('typecheck', 'typecheck'),
|
||||||
lintGate(),
|
lintGate(),
|
||||||
|
pnpmScript('duplication', 'duplication'),
|
||||||
coverageGate(),
|
coverageGate(),
|
||||||
pnpmScript('snapshot', 'test:snapshot'),
|
pnpmScript('snapshot', 'test:snapshot'),
|
||||||
demoSmokeGate({ needs: ['lint'] }),
|
demoSmokeGate({ needs: ['lint'] }),
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue