@@ -395,87 +395,100 @@ export class NotebookLMClient {
395395 return emptyResult ;
396396 }
397397
398- // Response format (from notebooklm-py _research.py):
399- // result = [[task_data, ...], ...] or [task_data, ...]
400- // task_data = [task_id, task_info]
401- // task_info = [?, query_info, ?, sources_and_summary, status_code]
402- // query_info = [query_text, ...]
403- // sources_and_summary = [sources_array, summary_text]
404- // Research status: 1=in_progress, 2=completed
398+ // Response format (from live API debugging):
399+ // result = [[[reportId, taskInfo, startTimestamp, updateTimestamp]]]
400+ // taskInfo = [notebookId, [query, sourceType], mode, null, statusCode, researchMeta]
401+ // researchMeta = [taskId, null, ?, null, "deep_research.v3p1s.prod"]
402+ // statusCode: 1=in_progress, 2=completed
403+ //
404+ // When completed, taskInfo has additional data:
405+ // taskInfo = [notebookId, [query, sourceType], mode, sourcesAndSummary, statusCode, researchMeta]
406+ // sourcesAndSummary = [sourcesArray, summaryText]
407+
408+ // Unwrap the triple-nesting to get to the task array
409+ // result = [[[...]]] → task = result[0][0]
410+ let task : unknown [ ] | null = null ;
405411
406- // Unwrap if double-nested
407- let tasks = resultArr ;
408412 if (
409- Array . isArray ( tasks [ 0 ] ) &&
410- tasks [ 0 ] . length > 0 &&
411- Array . isArray ( tasks [ 0 ] [ 0 ] ) &&
412- tasks [ 0 ] [ 0 ] . length > 0 &&
413- Array . isArray ( tasks [ 0 ] [ 0 ] [ 0 ] )
414- ) {
415- tasks = tasks [ 0 ] as unknown [ ] ;
416- } else if (
417- Array . isArray ( tasks [ 0 ] ) &&
418- tasks [ 0 ] . length > 0 &&
419- typeof tasks [ 0 ] [ 0 ] === 'string'
413+ Array . isArray ( resultArr [ 0 ] ) &&
414+ Array . isArray ( ( resultArr [ 0 ] as unknown [ ] ) [ 0 ] )
420415 ) {
421- // Already at task level: [[taskId, taskInfo], ...]
422- // wrap in array for uniform processing
423- tasks = [ tasks ] ;
416+ const inner = ( resultArr [ 0 ] as unknown [ ] ) [ 0 ] as unknown [ ] ;
417+ if ( inner . length >= 2 && typeof inner [ 0 ] === 'string' ) {
418+ task = inner ;
419+ }
424420 }
425421
426- // Find the most recent task
427- for ( const taskData of tasks ) {
428- if ( ! Array . isArray ( taskData ) || taskData . length < 2 ) continue ;
429-
430- const taskId = taskData [ 0 ] ;
431- const taskInfo = taskData [ 1 ] ;
432-
433- if ( typeof taskId !== 'string' || ! Array . isArray ( taskInfo ) ) continue ;
434-
435- const queryInfo = taskInfo [ 1 ] ;
436- const sourcesAndSummary = taskInfo [ 3 ] ;
437- const statusCode = taskInfo [ 4 ] ;
438-
439- const queryText =
440- Array . isArray ( queryInfo ) && typeof queryInfo [ 0 ] === 'string'
441- ? queryInfo [ 0 ]
442- : '' ;
443-
444- // Parse sources
445- const sources : Array < { url : string ; title : string } > = [ ] ;
446- let summary = '' ;
447-
448- if ( Array . isArray ( sourcesAndSummary ) && sourcesAndSummary . length >= 1 ) {
449- const sourcesData = Array . isArray ( sourcesAndSummary [ 0 ] )
450- ? sourcesAndSummary [ 0 ]
451- : [ ] ;
452- if (
453- sourcesAndSummary . length >= 2 &&
454- typeof sourcesAndSummary [ 1 ] === 'string'
455- ) {
456- summary = sourcesAndSummary [ 1 ] ;
457- }
458-
459- for ( const src of sourcesData ) {
460- if ( ! Array . isArray ( src ) || src . length < 2 ) continue ;
461- const url =
462- typeof src [ 0 ] === 'string' ? src [ 0 ] : '' ;
463- const title =
464- typeof src [ 1 ] === 'string' ? src [ 1 ] : '' ;
465- if ( title || url ) {
466- sources . push ( { url, title } ) ;
467- }
468- }
422+ if ( ! task ) {
423+ // Try single-nested: result = [[taskId, taskInfo, ...]]
424+ if (
425+ Array . isArray ( resultArr [ 0 ] ) &&
426+ typeof ( resultArr [ 0 ] as unknown [ ] ) [ 0 ] === 'string'
427+ ) {
428+ task = resultArr [ 0 ] as unknown [ ] ;
469429 }
430+ }
431+
432+ if ( ! task || task . length < 2 ) {
433+ return emptyResult ;
434+ }
470435
471- // Research status: 1=in_progress, 2=completed
472- const status : ResearchResult [ 'status' ] =
473- statusCode === 2 ? 'completed' : 'in_progress' ;
436+ const reportId = task [ 0 ] as string ;
437+ const taskInfo = task [ 1 ] as unknown [ ] ;
474438
475- return { taskId, status, query : queryText , sources, summary } ;
439+ if ( ! Array . isArray ( taskInfo ) || taskInfo . length < 5 ) {
440+ return emptyResult ;
441+ }
442+
443+ // taskInfo = [notebookId, [query, sourceType], mode, sourcesAndSummary, statusCode, researchMeta]
444+ const queryInfo = taskInfo [ 1 ] ;
445+ const sourcesAndSummary = taskInfo [ 3 ] ;
446+ const statusCode = taskInfo [ 4 ] ;
447+ const researchMeta = taskInfo [ 5 ] ;
448+
449+ const queryText =
450+ Array . isArray ( queryInfo ) && typeof queryInfo [ 0 ] === 'string'
451+ ? queryInfo [ 0 ]
452+ : '' ;
453+
454+ // Extract the research task ID from researchMeta
455+ let taskId = reportId ; // fallback to reportId
456+ if ( Array . isArray ( researchMeta ) && typeof researchMeta [ 0 ] === 'string' ) {
457+ taskId = researchMeta [ 0 ] ;
476458 }
477459
478- return emptyResult ;
460+ // Parse sources
461+ const sources : Array < { url : string ; title : string } > = [ ] ;
462+ let summary = '' ;
463+
464+ if ( Array . isArray ( sourcesAndSummary ) && sourcesAndSummary . length >= 1 ) {
465+ const sourcesData = Array . isArray ( sourcesAndSummary [ 0 ] )
466+ ? sourcesAndSummary [ 0 ]
467+ : [ ] ;
468+ if (
469+ sourcesAndSummary . length >= 2 &&
470+ typeof sourcesAndSummary [ 1 ] === 'string'
471+ ) {
472+ summary = sourcesAndSummary [ 1 ] ;
473+ }
474+
475+ for ( const src of sourcesData ) {
476+ if ( ! Array . isArray ( src ) || src . length < 2 ) continue ;
477+ const url =
478+ typeof src [ 0 ] === 'string' ? src [ 0 ] : '' ;
479+ const title =
480+ typeof src [ 1 ] === 'string' ? src [ 1 ] : '' ;
481+ if ( title || url ) {
482+ sources . push ( { url, title } ) ;
483+ }
484+ }
485+ }
486+
487+ // Research status: 1=in_progress, 2=completed
488+ const status : ResearchResult [ 'status' ] =
489+ statusCode === 2 ? 'completed' : 'in_progress' ;
490+
491+ return { taskId, status, query : queryText , sources, summary } ;
479492 }
480493
481494 /**
0 commit comments