code wiki / (root) / nx_aimode_lib.nx

nx_aimode_lib.nx source

↩ module page · 384 lines · 21182 B

1// nx_aimode_lib.nx -- BR50: read a Google AI Mode share link as its ANSWER TEXT, never as a script shell. 2// 3// WHAT THE WIRE SHOWS (measured 2026-09-18/19 with the CDP oracle ac_cdp/aimode_oracle over the operator's 4// share https://share.google/aimode/vVS1jO66c6HI3D4YN, network capture + DOM poll): 5// * share.google/aimode/<id> --302--> www.google.com/share.google?q=aimode/<id> --301--> 6// www.google.com/search?...&udm=50... (a 92,807 B script shell, title "Google Search", noscript 7// enable-JS redirect, five scripts, NO answer text). A COLD fetch of that /search url (no cookies) 8// returns the same shell -- proven twice sovereignly (nx_research_fetch). 9// * The FIRST /search response SETS cookies. A SECOND, cookie-WARMED GET of the same /search url 10// (the oracle's idx=1, cookie_header_len 0->1812) returns a 406,554 B server-rendered document that 11// is NOT the shell: it carries the AI Mode streaming containers AND, server-rendered as plain text in 12// its markup, the continuation URL /async/folwr?... (garc/mlro/elrc/_xsrf tokens all present -- 12 13// hits; the cold shell has ZERO). NO JavaScript computed those tokens: they are in the SSR HTML. 14// * A GET of that /async/folwr url (with the same cookie jar) returns a 1,452,920 B HTML fragment that 15// carries the WHOLE conversation's answer prose ("Neo-Hookean", "Hyperelastic", "Tyranny", 16// "Neuromorphic", "Tesla" all present). This is the ANSWER, delivered in ONE synchronous response -- 17// no streaming wait is needed on the sovereign side (the referee browser waits; we do not). 18// * The oracle was NOT signed in ("Sign in" in the nav) and no request that carries the answer is a POST 19// or a batchexecute with a JS-signed body: every answer-bearing request is a plain same-origin GET. 20// 21// SO THE SOVEREIGN PATH IS THREE COOKIE-SHARING GETs, no js_eval (BR3), no signed session: 22// PASS A follow share.google -> ... -> /search (cold), collecting Set-Cookie into a jar, capture final url 23// PASS B warm GET the final /search url WITH the jar -> extract the /async/folwr continuation url 24// PASS C GET /async/folwr WITH the jar -> the answer HTML fragment -> nx_html_to_text 25// A sign-in / consent wall is a NAMED refusal; a shell that never yields a continuation or answer is 26// REFUSED-NO-ANSWER by name; an empty body is REFUSED-EMPTY-BODY. 27// 28// COMPOSES INCUMBENTS, DUPLICATES NOTHING: the TLS-1.3 session, redirect Location resolution, cookie jar, 29// gzip and chunked decoders are the estate's (nx_https_fetch_follow's ff_path/ff_resolve_location/ 30// ff_collect_cookies/ff_gunzip, nx_https_get_complete's req driver, nx_http_response_parse/nx_http_dechunk, 31// nx_https_redirect). This file writes only the jar-keeping loop those helpers were never wired into, and 32// the answer parser. ms_find (balanced-substring) is nx_media_state's. 33// license_tier: ORIGINAL No hw writes (Rule 26). 34import "nx_syscalls.nx" 35import "nx_x509_trust_store.nx" 36import "nx_trust_store_load_from_certdata.nx" 37import "nx_https_fetch_follow.nx" // brings the whole HTTPS closure: url_for_fetch/connect, tls session, 38 // nx_https_req_complete, nx_http_response_parse, nx_http_dechunk, 39 // nx_redir_is_redirect/location, ff_path/ff_resolve_location/ 40 // ff_collect_cookies/ff_gunzip, nx_csprng_fill, TrustStore/NxHttpsTarget 41import "nx_html_to_text.nx" // nx_html_to_text: tags stripped, entities decoded, whitespace collapsed 42import "nx_media_state.nx" // ms_find: string-aware substring search 43 44// ---- status / refusal codes (br_share_read return value) ---- 45const AM_OK: i64 = 0 46const AM_SIGNIN: i64 = 10 // REFUSED-SIGNIN 47const AM_CONSENT: i64 = 11 // REFUSED-CONSENT-WALL 48const AM_NO_ANSWER: i64 = 12 // REFUSED-NO-ANSWER (shell never populated / async carried no answer) 49const AM_EMPTY: i64 = 13 // REFUSED-EMPTY-BODY 50const AM_FETCH: i64 = 14 // a pass could not fetch at all (connect/handshake/parse) 51const AM_NOFINAL: i64 = 15 // redirects never settled on a 200 52const AM_NOCONT: i64 = 16 // warm doc carried no /async/folwr continuation AND no inline answer 53 54// ---- sizes ---- 55const AM_RAW: i64 = 12582912 // 12 MB raw response reserve per hop (async decoded ~1.45MB; gzip smaller) 56const AM_JAR: i64 = 8192 // cookie jar (n=v; n=v) 57const AM_URL: i64 = 16384 // a resolved url (google search urls are long) 58const AM_HOPS: i64 = 8 // share.google->/share.google->/search is 2; head-room for extra bounces 59const AM_MINANS: i64 = 400 // an answer shorter than this is not an answer (a shell yields ~little text) 60 61const AM_CHROME_UA: *u8 = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.0.0 Safari/537.36" 62 63func am_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 64func am_cat(dst: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[o] = s[i]; o = o + 1; i = i + 1 } return o } 65func am_catn(dst: *u8, o: i64, v: i64) -> i64 { 66 if v == 0 { dst[o] = 48 as u8; return o + 1 } 67 let t: *u8 = sys_mmap(24); var m: i64 = v; var k: i64 = 0 68 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 69 var j: i64 = k - 1 70 while j >= 0 { dst[o] = t[j]; o = o + 1; j = j - 1 } 71 return o 72} 73 74// Case-insensitive substring test of a lowercase literal `pat` inside buf[0..n). Returns 1/0. 75func am_has_ci(buf: *u8, n: i64, pat: *u8) -> i64 { 76 let pl: i64 = am_slen(pat) 77 if pl == 0 { return 0 } 78 var i: i64 = 0 79 let last: i64 = n - pl 80 while i <= last { 81 var j: i64 = 0 82 var m: i64 = 1 83 while j < pl { 84 var c: i64 = buf[i+j] as i64 85 if c >= 65 { if c <= 90 { c = c + 32 } } 86 if c != (pat[j] as i64) { m = 0; j = pl } else { j = j + 1 } 87 } 88 if m == 1 { return 1 } 89 i = i + 1 90 } 91 return 0 92} 93 94// Build a GET request for host/path with the cookie jar (jl bytes) and an optional extra header line 95// (xhdr, NUL-terminated CRLF-terminated, or 0). Advertises gzip (ff_gunzip decodes it). Connection: close 96// so the body is everything after the header terminator (no keep-alive framing to track). 97func am_build_get(req: *u8, host: *u8, hlen: i64, path: *u8, plen: i64, jar: *u8, jl: i64, xhdr: *u8) -> i64 { 98 var o: i64 = am_cat(req, 0, "GET " as *u8) 99 var i: i64 = 0 100 while i < plen { req[o] = path[i]; o = o + 1; i = i + 1 } 101 o = am_cat(req, o, " HTTP/1.1\r\nHost: " as *u8) 102 i = 0 103 while i < hlen { req[o] = host[i]; o = o + 1; i = i + 1 } 104 o = am_cat(req, o, "\r\nUser-Agent: " as *u8) 105 o = am_cat(req, o, AM_CHROME_UA) 106 o = am_cat(req, o, "\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.9\r\nAccept-Encoding: gzip\r\n" as *u8) 107 if (xhdr as i64) != 0 { o = am_cat(req, o, xhdr) } 108 if jl > 0 { 109 o = am_cat(req, o, "Cookie: " as *u8) 110 i = 0 111 while i < jl { req[o] = jar[i]; o = o + 1; i = i + 1 } 112 o = am_cat(req, o, "\r\n" as *u8) 113 } 114 o = am_cat(req, o, "Connection: close\r\n\r\n" as *u8) 115 return o 116} 117 118// One HTTP hop over sovereign TLS-1.3 to the absolute https url in `urlbuf`. Sends the GET (with jar+xhdr), 119// parses the response, folds Set-Cookie into the jar. On a 3xx it writes the resolved next url into 120// next_out and returns AM_REDIR-marker (-1). On a final response it decodes the body into out and returns 121// body bytes (>=0), status in status_out[0]. On transport failure returns a negative code (< -1). 122const AM_HOP_REDIR: i64 = 0 - 1 123func am_hop(urlbuf: *u8, store: *TrustStore, jar: *u8, jl: *i64, xhdr: *u8, 124 out: *u8, out_cap: i64, status_out: *i64, next_out: *u8) -> i64 { 125 let target_raw: *u8 = sys_mmap(64) 126 let target: *NxHttpsTarget = target_raw as *NxHttpsTarget 127 target.url = nx_url_new() 128 target.port = 0 129 if nx_https_url_for_fetch(urlbuf, target) != NX_HTTPS_URL_OK { return 0 - AM_FETCH } 130 let fd_p: *i64 = (sys_mmap(16)) as *i64 131 if nx_https_url_connect(target, urlbuf, sys_now_realtime_sec(), fd_p) != NX_HTTPS_CONNECT_OK { return 0 - AM_FETCH } 132 let fd: i64 = fd_p[0] 133 sys_set_socket_timeout(fd, 12) 134 let cr: *u8 = sys_mmap(32); nx_csprng_fill(cr, 32) 135 let pk: *u8 = sys_mmap(32); nx_csprng_fill(pk, 32) 136 let vc_raw: *u8 = sys_mmap(64) 137 let vc: *TlsValidationContext = vc_raw as *TlsValidationContext 138 vc.store = store 139 vc.sni_host = urlbuf + target.url.host_off 140 vc.sni_host_len = target.url.host_len 141 vc.now_epoch = sys_now_realtime_sec() 142 let sr: i64 = nx_tls13_client_session_run(fd, urlbuf + target.url.host_off, target.url.host_len, cr, pk, vc) 143 if sr <= 0 { sys_close(fd); return 0 - AM_FETCH } 144 let session: *Tls13ClientSession = sr as *Tls13ClientSession 145 let path: *u8 = sys_mmap(4096) 146 let plen: i64 = ff_path(urlbuf, target, path) 147 let req: *u8 = sys_mmap(AM_JAR + 8192) 148 let req_len: i64 = am_build_get(req, urlbuf + target.url.host_off, target.url.host_len, path, plen, jar, jl[0], xhdr) 149 let buf: *u8 = sys_mmap(AM_RAW) 150 let gc: i64 = nx_https_req_complete(session, fd, req, req_len, buf, AM_RAW) 151 sys_close(fd) 152 if gc <= 0 { return 0 - AM_FETCH } 153 let r: *i64 = (sys_mmap(128)) as *i64 154 if nx_http_response_parse(buf, gc, r) != 0 { return 0 - AM_FETCH } 155 let status: i64 = r[1] 156 status_out[0] = status 157 ff_collect_cookies(buf, gc, jar, jl, AM_JAR) 158 if nx_redir_is_redirect(status) == 1 { 159 let loc: *u8 = sys_mmap(AM_URL) 160 let ln: i64 = nx_redir_location(buf, gc, loc, AM_URL) 161 if ln <= 0 { return 0 - AM_NOFINAL } 162 ff_resolve_location(loc, urlbuf, target, next_out) 163 return AM_HOP_REDIR 164 } 165 let body_off: i64 = r[6] 166 if r[10] == 1 { 167 let dn: i64 = nx_http_dechunk(buf + body_off, gc - body_off, out, out_cap) 168 if dn < 0 { return 0 - AM_FETCH } 169 return ff_gunzip(out, dn, out_cap) 170 } 171 var blen: i64 = gc - body_off 172 if blen < 0 { blen = 0 } 173 if blen > out_cap { blen = out_cap } 174 var c: i64 = 0 175 while c < blen { out[c] = buf[body_off + c]; c = c + 1 } 176 return ff_gunzip(out, blen, out_cap) 177} 178 179// Follow redirects from `start_url` (cold or warmed by the jar) to the first non-3xx response. On success 180// writes the FINAL url into final_out and returns the body bytes (final response body in `out`). 181func am_follow(start_url: *u8, store: *TrustStore, jar: *u8, jl: *i64, xhdr: *u8, 182 out: *u8, out_cap: i64, status_out: *i64, final_out: *u8) -> i64 { 183 let cur: *u8 = sys_mmap(AM_URL) 184 var i: i64 = 0 185 while start_url[i] != (0 as u8) { cur[i] = start_url[i]; i = i + 1 } 186 cur[i] = 0 as u8 187 let nxt: *u8 = sys_mmap(AM_URL) 188 var hop: i64 = 0 189 while hop < AM_HOPS { 190 let n: i64 = am_hop(cur, store, jar, jl, xhdr, out, out_cap, status_out, nxt) 191 if n == AM_HOP_REDIR { 192 var k: i64 = 0 193 while nxt[k] != (0 as u8) { cur[k] = nxt[k]; k = k + 1 } 194 cur[k] = 0 as u8 195 hop = hop + 1 196 } else { 197 if n < 0 { return n } 198 var k: i64 = 0 199 while cur[k] != (0 as u8) { final_out[k] = cur[k]; k = k + 1 } 200 final_out[k] = 0 as u8 201 return n 202 } 203 } 204 return 0 - AM_NOFINAL 205} 206 207// Extract the server-rendered /async/folwr continuation url out of the warm search document into out as an 208// ABSOLUTE https url on www.google.com. Un-escapes the three ways the url is escaped in the markup: 209// &amp; -> & & -> & \/ -> / (a bare backslash otherwise ends the run). Returns len or 0. 210func am_extract_folwr(doc: *u8, n: i64, out: *u8) -> i64 { 211 let mark: *u8 = "/async/folwr?" as *u8 212 let mpos: i64 = ms_find(doc, n, mark, 13, 0) 213 if mpos < 0 { return 0 } 214 var o: i64 = am_cat(out, 0, "https://www.google.com" as *u8) 215 var i: i64 = mpos 216 var go: i64 = 1 217 while go == 1 { 218 if i >= n { go = 0 } 219 else { 220 let c: i64 = doc[i] & 0xff 221 if c == 34 { go = 0 } // " 222 else { if c == 39 { go = 0 } // ' 223 else { if c == 60 { go = 0 } // < 224 else { if c == 32 { go = 0 } // space 225 else { if c == 9 { go = 0 } 226 else { if c == 10 { go = 0 } 227 else { if c == 13 { go = 0 } 228 else { 229 if c == 38 { // & : maybe &amp; 230 if i + 4 < n { if (doc[i+1]&0xff)==97 { if (doc[i+2]&0xff)==109 { if (doc[i+3]&0xff)==112 { if (doc[i+4]&0xff)==59 { 231 out[o] = 38 as u8; o = o + 1; i = i + 5 232 } else { out[o]=38 as u8; o=o+1; i=i+1 } } else { out[o]=38 as u8; o=o+1; i=i+1 } } else { out[o]=38 as u8; o=o+1; i=i+1 } } else { out[o]=38 as u8; o=o+1; i=i+1 } } 233 else { out[o]=38 as u8; o=o+1; i=i+1 } 234 } 235 else { if c == 92 { // backslash: \/ or & else stop 236 if i + 1 < n { 237 let d: i64 = doc[i+1] & 0xff 238 if d == 47 { out[o]=47 as u8; o=o+1; i=i+2 } // \/ 239 else { if d == 117 { // \u 240 if i + 5 < n { if (doc[i+2]&0xff)==48 { if (doc[i+3]&0xff)==48 { if (doc[i+4]&0xff)==50 { if (doc[i+5]&0xff)==54 { 241 out[o]=38 as u8; o=o+1; i=i+6 // & -> & 242 } else { go=0 } } else { go=0 } } else { go=0 } } else { go=0 } } 243 else { go = 0 } } 244 else { go = 0 } } 245 } else { go = 0 } 246 } 247 else { out[o] = c as u8; o = o + 1; i = i + 1 } } 248 } } } } } } } 249 } 250 } 251 out[o] = 0 as u8 252 return o 253} 254 255// Count AND list citation references in an answer HTML fragment. Google AI Mode renders each citation as a 256// link that beacons the click to /url via a ping="/url?...&url=<opaque>" attribute; the destination is an 257// OPAQUE server-side redirect token (url=CAES...), not a plaintext URL, and the visible host is JS-decorated, 258// so the ping BEACON is the stable server-rendered citation signal. This counts those beacons and, if 259// sink != 0, appends each beacon reference (with &amp; decoded to &) one per line. MEASURED PROXY, declared: 260// it counts citation-beacon links; it does not de-duplicate multi-source chips or resolve the opaque token. 261func am_citations(doc: *u8, n: i64, sink: *u8, sink_cap: i64) -> i64 { 262 let mark: *u8 = "ping=\"/url" as *u8 263 let ml: i64 = am_slen(mark) 264 var cnt: i64 = 0 265 var so: i64 = 0 266 var i: i64 = 0 267 let last: i64 = n - ml 268 while i <= last { 269 if ms_find(doc, i + ml, mark, ml, i) == i { 270 cnt = cnt + 1 271 if (sink as i64) != 0 { 272 // copy from after ping=" up to the closing quote, decoding &amp; -> &, into the sink line. 273 var j: i64 = i + 6 // past ping="; retain the leading slash 274 var go: i64 = 1 275 while go == 1 { 276 if j >= n { go = 0 } 277 else { 278 let c: i64 = doc[j] & 0xff 279 if c == 34 { go = 0 } 280 else { 281 if c == 38 { // &amp; -> & 282 if j + 4 < n { if (doc[j+1]&0xff)==97 { if (doc[j+2]&0xff)==109 { if (doc[j+3]&0xff)==112 { if (doc[j+4]&0xff)==59 { j = j + 4 } } } } } 283 } 284 if so < sink_cap - 2 { sink[so] = c as u8; so = so + 1 } 285 j = j + 1 286 } 287 } 288 } 289 if so < sink_cap - 1 { sink[so] = 10 as u8; so = so + 1 } 290 } 291 i = i + ml 292 } else { i = i + 1 } 293 } 294 if (sink as i64) != 0 { if so < sink_cap { sink[so] = 0 as u8 } } 295 return cnt 296} 297 298// A response that is the enable-JS SHELL, not an answer: title "Google Search" + the noscript enablejs 299// redirect and no answer container. Used to classify the warm doc / async body. 300func am_is_shell(buf: *u8, n: i64) -> i64 { 301 if am_has_ci(buf, n, "httpservice/retry/enablejs" as *u8) == 1 { return 1 } 302 return 0 303} 304func am_is_signin(buf: *u8, n: i64) -> i64 { 305 if am_has_ci(buf, n, "accounts.google.com/servicelogin" as *u8) == 1 { return 1 } 306 if am_has_ci(buf, n, "sign in to continue" as *u8) == 1 { return 1 } 307 return 0 308} 309func am_is_consent(buf: *u8, n: i64) -> i64 { 310 if am_has_ci(buf, n, "consent.google.com" as *u8) == 1 { return 1 } 311 if am_has_ci(buf, n, "before you continue" as *u8) == 1 { return 1 } 312 return 0 313} 314 315func am_dbg(a: *u8, x: i64, b: *u8, y: i64, c: *u8, z: i64) -> i64 { 316 sys_write(2, a, am_slen(a)); let t: *u8 = sys_mmap(24); var o: i64 = am_catn(t, 0, x); sys_write(2, t, o) 317 sys_write(2, b, am_slen(b)); o = am_catn(t, 0, y); sys_write(2, t, o) 318 sys_write(2, c, am_slen(c)); o = am_catn(t, 0, z); sys_write(2, t, o) 319 sys_write(2, "\n" as *u8, 1); return 0 320} 321func am_dbg_url(a: *u8, u: *u8) -> i64 { sys_write(2, a, am_slen(a)); sys_write(2, u, am_slen(u)); sys_write(2, "\n" as *u8, 1); return 0 } 322 323// br_share_read: the BR50 contract. Reads the AI Mode share link at `share_url` and writes the extracted 324// answer TEXT (tags stripped, entities decoded) into out[0..out_cap), NUL-terminated; ncit_out[0] = citation 325// count; textlen_out[0] = answer text length. Returns AM_OK or a NAMED refusal/error code above. 326// `dbg`=1 prints one-line per-pass diagnostics to stderr (final urls, sizes, status). 327func br_share_read(share_url: *u8, store: *TrustStore, out: *u8, out_cap: i64, 328 cites_out: *u8, cites_cap: i64, final_out: *u8, final_cap: i64, 329 ncit_out: *i64, textlen_out: *i64, dbg: i64) -> i64 { 330 ncit_out[0] = 0 331 textlen_out[0] = 0 332 if (cites_out as i64) != 0 { cites_out[0] = 0 as u8 } 333 if (final_out as i64) != 0 { final_out[0] = 0 as u8 } 334 let jar: *u8 = sys_mmap(AM_JAR) 335 let jl: *i64 = (sys_mmap(8)) as *i64 336 jl[0] = 0 337 let st: *i64 = (sys_mmap(8)) as *i64 338 let finalu: *u8 = sys_mmap(AM_URL) 339 let warmu: *u8 = sys_mmap(AM_URL) 340 let folwr: *u8 = sys_mmap(AM_URL) 341 let doc: *u8 = sys_mmap(AM_RAW) 342 343 // PASS A: cold follow share.google -> ... -> /search (the 200 shell); collect Set-Cookie; capture final url. 344 let na: i64 = am_follow(share_url, store, jar, jl, 0 as *u8, doc, AM_RAW, st, finalu) 345 if dbg == 1 { am_dbg("PASS-A status=" as *u8, st[0], " bytes=" as *u8, na, " cookies=" as *u8, jl[0]); am_dbg_url("PASS-A final=" as *u8, finalu) } 346 if na < 0 { return AM_FETCH } 347 if (final_out as i64) != 0 { var fu: i64 = 0; while finalu[fu] != (0 as u8) { if fu < final_cap - 1 { final_out[fu] = finalu[fu] } fu = fu + 1 } final_out[fu] = 0 as u8 } 348 if am_is_signin(doc, na) == 1 { return AM_SIGNIN } 349 if am_is_consent(doc, na) == 1 { return AM_CONSENT } 350 351 // PASS B: warm GET the final /search url WITH the jar -> the SSR document that carries the continuation. 352 let nb: i64 = am_follow(finalu, store, jar, jl, 0 as *u8, doc, AM_RAW, st, warmu) 353 if dbg == 1 { am_dbg("PASS-B status=" as *u8, st[0], " bytes=" as *u8, nb, " cookies=" as *u8, jl[0]) } 354 if nb < 0 { return AM_FETCH } 355 if nb == 0 { return AM_EMPTY } 356 if am_is_signin(doc, nb) == 1 { return AM_SIGNIN } 357 if am_is_consent(doc, nb) == 1 { return AM_CONSENT } 358 359 // extract the server-rendered /async/folwr continuation url from the warm doc (BEFORE doc is reused). 360 let fl: i64 = am_extract_folwr(doc, nb, folwr) 361 if dbg == 1 { if fl > 0 { am_dbg_url("PASS-B folwr=" as *u8, folwr) } else { am_dbg_url("PASS-B folwr=" as *u8, "<none>" as *u8) } } 362 if fl == 0 { 363 // No continuation. The answer may be inline in the warm doc (a fully-SSR'd share); try it, else NO-ANSWER. 364 if am_is_shell(doc, nb) == 1 { return AM_NO_ANSWER } 365 let tlz: i64 = nx_html_to_text(doc, nb, out, out_cap) 366 if tlz >= AM_MINANS { ncit_out[0] = am_citations(doc, nb, cites_out, cites_cap); textlen_out[0] = tlz; return AM_OK } 367 return AM_NOCONT 368 } 369 370 // PASS C: GET /async/folwr WITH the jar (+ Referer) -> the answer HTML fragment. 371 let nc: i64 = am_follow(folwr, store, jar, jl, "Referer: https://www.google.com/\r\n" as *u8, doc, AM_RAW, st, warmu) 372 if dbg == 1 { am_dbg("PASS-C status=" as *u8, st[0], " bytes=" as *u8, nc, " cookies=" as *u8, jl[0]) } 373 if nc < 0 { return AM_FETCH } 374 if nc == 0 { return AM_EMPTY } 375 if am_is_signin(doc, nc) == 1 { return AM_SIGNIN } 376 if am_is_consent(doc, nc) == 1 { return AM_CONSENT } 377 378 let ncit: i64 = am_citations(doc, nc, cites_out, cites_cap) 379 let tl: i64 = nx_html_to_text(doc, nc, out, out_cap) 380 ncit_out[0] = ncit 381 textlen_out[0] = tl 382 if tl < AM_MINANS { return AM_NO_ANSWER } 383 return AM_OK 384}