@@ -7,6 +7,7 @@ import os from "node:os";
77import path from "node:path" ;
88import process from "node:process" ;
99import { pathToFileURL } from "node:url" ;
10+ import { stripLeadingPackageManagerSeparator } from "./lib/arg-utils.mjs" ;
1011import { readBoundedResponseText } from "./lib/bounded-response.mjs" ;
1112
1213const ISSUE_FILE_COUNTS = [
@@ -63,6 +64,28 @@ Options:
6364}
6465
6566const NON_NEGATIVE_INTEGER_PATTERN = / ^ ( 0 | [ 1 - 9 ] \d * ) $ / u;
67+ const ARGUMENT_FLAGS = new Set ( [
68+ "--allow-non-darwin" ,
69+ "--expect-leak" ,
70+ "--files" ,
71+ "--full" ,
72+ "--help" ,
73+ "--invoke-timeout-ms" ,
74+ "--keep" ,
75+ "--max-workspace-reg-fds" ,
76+ "--min-leaked-fds" ,
77+ "--mode" ,
78+ "--output-dir" ,
79+ "--report-only" ,
80+ "--sample-delay-ms" ,
81+ "--settle-delay-ms" ,
82+ ] ) ;
83+
84+ function stripPackageManagerSeparatorForKnownFlags ( argv ) {
85+ return argv [ 0 ] === "--" && ARGUMENT_FLAGS . has ( argv [ 1 ] )
86+ ? stripLeadingPackageManagerSeparator ( argv )
87+ : argv ;
88+ }
6689
6790export function readNumber ( value , label ) {
6891 const raw = String ( value ) . trim ( ) ;
@@ -95,6 +118,7 @@ function readPositiveNumberEnv(name, fallback) {
95118}
96119
97120export function parseArgs ( argv ) {
121+ const args = stripPackageManagerSeparatorForKnownFlags ( argv ) ;
98122 const stamp = new Date ( ) . toISOString ( ) . replace ( / [: .] / g, "-" ) ;
99123 const options = {
100124 fileCount : undefined ,
@@ -109,9 +133,9 @@ export function parseArgs(argv) {
109133 allowNonDarwin : process . env . OPENCLAW_MEMORY_FD_REPRO_ALLOW_NON_DARWIN === "1" ,
110134 } ;
111135
112- parseArgv: for ( let i = 0 ; i < argv . length ; i += 1 ) {
113- const arg = argv [ i ] ;
114- const next = argv [ i + 1 ] ;
136+ parseArgv: for ( let i = 0 ; i < args . length ; i += 1 ) {
137+ const arg = args [ i ] ;
138+ const next = args [ i + 1 ] ;
115139 const readValue = ( ) => {
116140 if ( ! next ) {
117141 throw new Error ( `Missing value for ${ arg } ` ) ;
@@ -435,6 +459,122 @@ export async function stopGatewayWithRuntime({
435459
436460export { readBoundedResponseText } ;
437461
462+ function parseJsonValue ( text ) {
463+ try {
464+ return JSON . parse ( text ) ;
465+ } catch {
466+ return null ;
467+ }
468+ }
469+
470+ function asRecord ( value ) {
471+ return value && typeof value === "object" && ! Array . isArray ( value ) ? value : null ;
472+ }
473+
474+ function readStringProperty ( record , key ) {
475+ const value = record ?. [ key ] ;
476+ return typeof value === "string" && value . trim ( ) ? value : undefined ;
477+ }
478+
479+ function parseToolTextContent ( result ) {
480+ const content = Array . isArray ( result ?. content ) ? result . content : [ ] ;
481+ for ( const entry of content ) {
482+ const text = entry ?. type === "text" && typeof entry . text === "string" ? entry . text : null ;
483+ if ( ! text ) {
484+ continue ;
485+ }
486+ const parsed = asRecord ( parseJsonValue ( text ) ) ;
487+ if ( parsed ) {
488+ return parsed ;
489+ }
490+ }
491+ return null ;
492+ }
493+
494+ export function classifyMemorySearchInvokeResponse ( { httpOk, status, bodyText } ) {
495+ const parsedBody = parseJsonValue ( bodyText ) ;
496+ const body = asRecord ( parsedBody ) ;
497+ if ( ! httpOk ) {
498+ const errorRecord = asRecord ( body ?. error ) ;
499+ return {
500+ ok : false ,
501+ httpOk,
502+ status,
503+ gatewayOk : body ?. ok === true ? true : body ?. ok === false ? false : undefined ,
504+ error :
505+ readStringProperty ( errorRecord , "message" ) ??
506+ readStringProperty ( body , "error" ) ??
507+ `memory_search HTTP request failed with status ${ status } ` ,
508+ } ;
509+ }
510+ if ( ! body ) {
511+ return {
512+ ok : false ,
513+ httpOk,
514+ status,
515+ error : "memory_search response was not JSON" ,
516+ } ;
517+ }
518+
519+ const gatewayOk = body . ok === true ? true : body . ok === false ? false : undefined ;
520+ if ( gatewayOk === false ) {
521+ const errorRecord = asRecord ( body . error ) ;
522+ return {
523+ ok : false ,
524+ httpOk,
525+ status,
526+ gatewayOk,
527+ error :
528+ readStringProperty ( errorRecord , "message" ) ??
529+ readStringProperty ( body , "error" ) ??
530+ "memory_search gateway invocation failed" ,
531+ } ;
532+ }
533+
534+ const result = asRecord ( body . result ) ;
535+ const details = asRecord ( result ?. details ) ;
536+ const directResult = Array . isArray ( result ?. results ) ? result : null ;
537+ const directBody =
538+ Array . isArray ( body . results ) || body . disabled === true || body . unavailable === true
539+ ? body
540+ : null ;
541+ const payload = details ?? parseToolTextContent ( result ) ?? directResult ?? directBody ;
542+ if ( ! payload ) {
543+ return {
544+ ok : false ,
545+ httpOk,
546+ status,
547+ gatewayOk,
548+ error : "memory_search result payload missing or invalid" ,
549+ } ;
550+ }
551+ const resultCount = Array . isArray ( payload . results ) ? payload . results . length : undefined ;
552+ const toolDisabled = payload . disabled === true ;
553+ const toolUnavailable = payload . unavailable === true ;
554+ const toolError = readStringProperty ( payload , "error" ) ;
555+ const ok = gatewayOk === true && ! toolDisabled && ! toolUnavailable && ! toolError ;
556+
557+ return {
558+ ok,
559+ httpOk,
560+ status,
561+ gatewayOk,
562+ resultCount,
563+ toolDisabled,
564+ toolUnavailable,
565+ ...( toolError ? { toolError } : { } ) ,
566+ ...( ok
567+ ? { }
568+ : {
569+ error :
570+ toolError ??
571+ ( toolDisabled || toolUnavailable
572+ ? "memory_search returned disabled/unavailable"
573+ : "memory_search result payload missing or invalid" ) ,
574+ } ) ,
575+ } ;
576+ }
577+
438578async function invokeMemorySearch ( { port, token, timeoutMs } ) {
439579 const controller = new AbortController ( ) ;
440580 const timer = setTimeout ( ( ) => controller . abort ( ) , timeoutMs ) ;
@@ -462,9 +602,13 @@ async function invokeMemorySearch({ port, token, timeoutMs }) {
462602 "memory_search" ,
463603 MEMORY_SEARCH_RESPONSE_MAX_BYTES ,
464604 ) ;
465- return {
466- ok : res . ok ,
605+ const result = classifyMemorySearchInvokeResponse ( {
606+ httpOk : res . ok ,
467607 status : res . status ,
608+ bodyText : text ,
609+ } ) ;
610+ return {
611+ ...result ,
468612 durationMs : Date . now ( ) - startedAt ,
469613 bodyPreview : text . slice ( 0 , 500 ) ,
470614 } ;
0 commit comments