mirror of
https://github.com/stablyai/orca.git
synced 2026-11-03 02:27:43 +00:00
* refactor(agent-status): isolate legacy status ingress * fix(agent-hooks): move advertised-capability source onto the ingest envelope ingestRemote() gained a third positional argument in this PR (advertisedAgentStatusCapabilities) to satisfy a new ratchet requiring every legacy-ingress call site to name its capability source. Both production callers pass the same constant every time, so the argument carries zero runtime information — but Vitest's toHaveBeenCalledWith matches argument count exactly, so the pre-existing SSH relay integration test (which asserts a 2-argument call) started failing even though nothing about the actual admission decision changed. Capabilities are a property of the producing peer/connection, not an orthogonal call parameter, so move the field onto the envelope object instead of adding a third positional argument: ingestRemote reads envelope.advertisedAgentStatusCapabilities (defaulting to the unadvertised-legacy-peer set), and both call sites stamp the constant onto their envelope literal. Call arity stays at two arguments, so the pre-existing evidence test needs no change. The envelope never crosses the wire in either caller: SSH rebuilds it field-by-field from the RPC params, and the WSL path copies (never mutates) the wire-deserialized notification before stamping the field on, so this is purely an internal main-process shape change. Also strengthens the ingress ratchet test that required this: it previously only checked that the capability constant's name appeared somewhere in each caller's source, which a stray unused import could satisfy. It now asserts the actual `advertisedAgentStatusCapabilities: AGENT_STATUS_LEGACY_UNADVERTISED_PEER_CAPABILITIES` key:value binding is present.
98 lines
3.3 KiB
TypeScript
98 lines
3.3 KiB
TypeScript
import {
|
|
blankStringContents,
|
|
blankStringContentsDesynced,
|
|
stripComments
|
|
} from './source-scan/source-tree-scan'
|
|
|
|
export type AgentStatusLegacyMutationBypass = {
|
|
kind: 'direct-mutation' | 'alias-mutation' | 'map-cast' | 'passed-map' | 'scan-desync'
|
|
detail: string
|
|
}
|
|
|
|
const MUTATOR_NAMES = '(?:set|delete|clear)'
|
|
const IDENTIFIER = '[A-Za-z_$][A-Za-z0-9_$]*'
|
|
|
|
function escapeRegExp(value: string): string {
|
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
}
|
|
|
|
function aliasesForLegacyStatusMap(source: string): Set<string> {
|
|
const aliases = new Set<string>()
|
|
const assignment = new RegExp(
|
|
`\\b(?:const|let|var)\\s+(${IDENTIFIER})\\s*=\\s*(?:this\\.)?state\\.lastStatusByPaneKey\\s*(?:;|\\n|$)`,
|
|
'g'
|
|
)
|
|
for (const match of source.matchAll(assignment)) {
|
|
aliases.add(match[1]!)
|
|
}
|
|
const destructuring = new RegExp(
|
|
`\\b(?:const|let|var)\\s*\\{[^}]*\\blastStatusByPaneKey(?:\\s*:\\s*(${IDENTIFIER}))?`,
|
|
'g'
|
|
)
|
|
for (const match of source.matchAll(destructuring)) {
|
|
aliases.add(match[1] ?? 'lastStatusByPaneKey')
|
|
}
|
|
return aliases
|
|
}
|
|
|
|
function isReadonlySnapshotCallPrefix(prefix: string): boolean {
|
|
return /(?:Array\.from|new\s+Map)\(\s*$/.test(prefix)
|
|
}
|
|
|
|
export function findAgentStatusLegacyMutationBypasses(
|
|
source: string
|
|
): AgentStatusLegacyMutationBypass[] {
|
|
const stripped = stripComments(source)
|
|
if (blankStringContentsDesynced(stripped)) {
|
|
return [{ kind: 'scan-desync', detail: 'source scanner lost quote or template state' }]
|
|
}
|
|
const code = blankStringContents(stripped)
|
|
const bypasses: AgentStatusLegacyMutationBypass[] = []
|
|
if (
|
|
new RegExp(`\\blastStatusByPaneKey\\s*\\.\\s*${MUTATOR_NAMES}\\s*\\(`).test(code) ||
|
|
/\[['"]lastStatusByPaneKey['"]\]\s*\.\s*(?:set|delete|clear)\s*\(/.test(stripped)
|
|
) {
|
|
bypasses.push({ kind: 'direct-mutation', detail: 'lastStatusByPaneKey mutator call' })
|
|
}
|
|
if (
|
|
new RegExp(
|
|
`\\blastStatusByPaneKey\\b[\\s\\S]{0,100}\\bas\\s+(?:unknown\\s+as\\s+)?(?:Readonly)?Map\\b[\\s\\S]{0,100}\\.\\s*${MUTATOR_NAMES}\\s*\\(`
|
|
).test(code)
|
|
) {
|
|
bypasses.push({ kind: 'map-cast', detail: 'lastStatusByPaneKey cast back to a mutable Map' })
|
|
}
|
|
|
|
const aliases = aliasesForLegacyStatusMap(code)
|
|
for (const alias of aliases) {
|
|
const escaped = escapeRegExp(alias)
|
|
if (new RegExp(`\\b${escaped}\\s*\\.\\s*${MUTATOR_NAMES}\\s*\\(`).test(code)) {
|
|
bypasses.push({ kind: 'alias-mutation', detail: `${alias} mutates an aliased status map` })
|
|
}
|
|
const passed = new RegExp(`\\b${IDENTIFIER}(?:\\.${IDENTIFIER})*\\s*\\(\\s*${escaped}\\b`, 'g')
|
|
for (const match of code.matchAll(passed)) {
|
|
const prefix = code.slice(
|
|
Math.max(0, match.index - 24),
|
|
match.index + match[0].indexOf('(') + 1
|
|
)
|
|
if (!isReadonlySnapshotCallPrefix(prefix)) {
|
|
bypasses.push({ kind: 'passed-map', detail: `${alias} is passed to another function` })
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
const directPass = new RegExp(
|
|
`\\b(${IDENTIFIER}(?:\\.${IDENTIFIER})*)\\s*\\(\\s*((?:this\\.)?state\\.lastStatusByPaneKey)\\s*[,)]`,
|
|
'g'
|
|
)
|
|
for (const match of code.matchAll(directPass)) {
|
|
if (match[1] !== 'Array.from' && match[1] !== 'Map') {
|
|
bypasses.push({
|
|
kind: 'passed-map',
|
|
detail: 'lastStatusByPaneKey is passed to another function'
|
|
})
|
|
}
|
|
}
|
|
return bypasses
|
|
}
|