@tiens.nguyen/gonext-local-worker 1.0.192 → 1.0.194
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/gonext_agent_chat.py +173 -57
- package/package.json +1 -1
package/gonext_agent_chat.py
CHANGED
|
@@ -3288,6 +3288,14 @@ def run_agent_chat(cfg):
|
|
|
3288
3288
|
text = (_last_obs.get("text") or "").strip()
|
|
3289
3289
|
if text:
|
|
3290
3290
|
_log(f"max-steps fallback (last tool obs) → {text[:80]}")
|
|
3291
|
+
# Without this, `text` (a tool's RAW return value — e.g. a bare grep_repo
|
|
3292
|
+
# dump of file:line hits) becomes the entire chat reply with zero framing,
|
|
3293
|
+
# reading as a bizarre non-answer. A short honest note makes clear this is
|
|
3294
|
+
# a partial result from an interrupted run, not the actual answer/edit.
|
|
3295
|
+
text = (
|
|
3296
|
+
"I ran out of steps before finishing this. Here's the last thing I "
|
|
3297
|
+
"found — reply to have me continue from here:\n\n" + text
|
|
3298
|
+
)
|
|
3291
3299
|
else:
|
|
3292
3300
|
# No usable tool output (e.g. every step's code failed to parse).
|
|
3293
3301
|
# Don't dead-end on a canned apology — answer from the conversation.
|
|
@@ -3344,12 +3352,18 @@ def run_agent_chat(cfg):
|
|
|
3344
3352
|
path, cached = _rag_download(url, (dest_path or "").strip())
|
|
3345
3353
|
if cached:
|
|
3346
3354
|
_emit({"type": "step", "text": "Already downloaded — reusing cached file"})
|
|
3347
|
-
|
|
3348
|
-
|
|
3355
|
+
out = (f"Already downloaded earlier — reusing {path} "
|
|
3356
|
+
f"({os.path.getsize(path)} bytes). Next: unzip_file(zip_path='{path}').")
|
|
3357
|
+
_last_obs["text"] = out
|
|
3358
|
+
return out
|
|
3349
3359
|
_emit({"type": "step", "text": f"Downloading → {url[:80]}"})
|
|
3350
|
-
|
|
3360
|
+
out = f"Downloaded to {path} ({os.path.getsize(path)} bytes). Next: unzip_file(zip_path='{path}') if it is a zip."
|
|
3361
|
+
_last_obs["text"] = out
|
|
3362
|
+
return out
|
|
3351
3363
|
except Exception as e: # noqa: BLE001
|
|
3352
|
-
|
|
3364
|
+
msg = f"Error: download failed: {type(e).__name__}: {e}"
|
|
3365
|
+
_last_obs["text"] = msg
|
|
3366
|
+
return msg
|
|
3353
3367
|
|
|
3354
3368
|
@tool
|
|
3355
3369
|
def unzip_file(zip_path: str, dest_dir: str = "") -> str:
|
|
@@ -3365,12 +3379,18 @@ def run_agent_chat(cfg):
|
|
|
3365
3379
|
more = f" …(+{len(names) - 15} more)" if len(names) > 15 else ""
|
|
3366
3380
|
if cached:
|
|
3367
3381
|
_emit({"type": "step", "text": "Already unzipped — reusing extracted files"})
|
|
3368
|
-
|
|
3369
|
-
|
|
3382
|
+
out = (f"Already unzipped earlier — {len(names)} files at {d}. "
|
|
3383
|
+
f"Files: {preview}{more}. Use list_dir/read_text_file or rag_index on it.")
|
|
3384
|
+
_last_obs["text"] = out
|
|
3385
|
+
return out
|
|
3370
3386
|
_emit({"type": "step", "text": "Unzipping…"})
|
|
3371
|
-
|
|
3387
|
+
out = f"Unzipped {len(names)} files to {d}. Files: {preview}{more}. Next: rag_index(path='{d}', source_url=<the original url>)."
|
|
3388
|
+
_last_obs["text"] = out
|
|
3389
|
+
return out
|
|
3372
3390
|
except Exception as e: # noqa: BLE001
|
|
3373
|
-
|
|
3391
|
+
msg = f"Error: unzip failed: {type(e).__name__}: {e}"
|
|
3392
|
+
_last_obs["text"] = msg
|
|
3393
|
+
return msg
|
|
3374
3394
|
|
|
3375
3395
|
@tool
|
|
3376
3396
|
def list_dir(path: str) -> str:
|
|
@@ -3383,7 +3403,9 @@ def run_agent_chat(cfg):
|
|
|
3383
3403
|
import os
|
|
3384
3404
|
root = _rag_read_allowed((path or ".").strip())
|
|
3385
3405
|
if os.path.isfile(root):
|
|
3386
|
-
|
|
3406
|
+
out = f"{root} is a file ({os.path.getsize(root)} bytes)."
|
|
3407
|
+
_last_obs["text"] = out
|
|
3408
|
+
return out
|
|
3387
3409
|
entries = []
|
|
3388
3410
|
for dirpath, dirnames, filenames in os.walk(root):
|
|
3389
3411
|
dirnames[:] = [d for d in dirnames if d not in _RAG_SKIP_DIRS]
|
|
@@ -3398,9 +3420,13 @@ def run_agent_chat(cfg):
|
|
|
3398
3420
|
break
|
|
3399
3421
|
entries.sort()
|
|
3400
3422
|
more = " …(truncated)" if len(entries) >= 300 else ""
|
|
3401
|
-
|
|
3423
|
+
out = f"{len(entries)} files under {root}:\n" + "\n".join(entries) + more
|
|
3424
|
+
_last_obs["text"] = out
|
|
3425
|
+
return out
|
|
3402
3426
|
except Exception as e: # noqa: BLE001
|
|
3403
|
-
|
|
3427
|
+
msg = f"Error: list_dir failed: {type(e).__name__}: {e}"
|
|
3428
|
+
_last_obs["text"] = msg
|
|
3429
|
+
return msg
|
|
3404
3430
|
|
|
3405
3431
|
@tool
|
|
3406
3432
|
def read_text_file(path: str, max_chars: int = 20000) -> str:
|
|
@@ -3414,14 +3440,22 @@ def run_agent_chat(cfg):
|
|
|
3414
3440
|
import os
|
|
3415
3441
|
rp = _rag_read_allowed((path or "").strip())
|
|
3416
3442
|
if not os.path.isfile(rp):
|
|
3417
|
-
|
|
3443
|
+
msg = f"Error: not a file: {path}"
|
|
3444
|
+
_last_obs["text"] = msg
|
|
3445
|
+
return msg
|
|
3418
3446
|
if os.path.getsize(rp) > 5 * 1024 * 1024:
|
|
3419
|
-
|
|
3447
|
+
msg = "Error: file too large to read (> 5 MB)."
|
|
3448
|
+
_last_obs["text"] = msg
|
|
3449
|
+
return msg
|
|
3420
3450
|
with open(rp, "r", encoding="utf-8", errors="replace") as fh:
|
|
3421
3451
|
data = fh.read(max(500, min(int(max_chars or 20000), 100000)))
|
|
3422
|
-
|
|
3452
|
+
out = data or "(empty file)"
|
|
3453
|
+
_last_obs["text"] = out
|
|
3454
|
+
return out
|
|
3423
3455
|
except Exception as e: # noqa: BLE001
|
|
3424
|
-
|
|
3456
|
+
msg = f"Error: read_text_file failed: {type(e).__name__}: {e}"
|
|
3457
|
+
_last_obs["text"] = msg
|
|
3458
|
+
return msg
|
|
3425
3459
|
|
|
3426
3460
|
@tool
|
|
3427
3461
|
def rag_index(path: str, source_url: str) -> str:
|
|
@@ -3432,7 +3466,9 @@ def run_agent_chat(cfg):
|
|
|
3432
3466
|
source_url: the original URL the user gave — used as the knowledge-base key.
|
|
3433
3467
|
"""
|
|
3434
3468
|
if not _RAG_AVAILABLE:
|
|
3435
|
-
|
|
3469
|
+
msg = "Error: RAG is not configured (enable it + add AWS credentials in Settings)."
|
|
3470
|
+
_last_obs["text"] = msg
|
|
3471
|
+
return msg
|
|
3436
3472
|
_emit({"type": "step", "text": f"RAG source → {source_url}"})
|
|
3437
3473
|
_emit({"type": "step", "text": "Indexing files for RAG…"})
|
|
3438
3474
|
try:
|
|
@@ -3448,7 +3484,9 @@ def run_agent_chat(cfg):
|
|
|
3448
3484
|
files += 1
|
|
3449
3485
|
records.extend(_rag_chunk_text(text, rel, ext))
|
|
3450
3486
|
if not records:
|
|
3451
|
-
|
|
3487
|
+
msg = "No indexable text files found at that path."
|
|
3488
|
+
_last_obs["text"] = msg
|
|
3489
|
+
return msg
|
|
3452
3490
|
for i in range(0, len(records), 64):
|
|
3453
3491
|
batch = records[i:i + 64]
|
|
3454
3492
|
vecs = _embed(rag_embed_base, rag_embed_model, [r["text"] for r in batch])
|
|
@@ -3469,10 +3507,14 @@ def run_agent_chat(cfg):
|
|
|
3469
3507
|
client.put_object(Bucket=bucket, Key=f"{base}/manifest.json",
|
|
3470
3508
|
Body=json.dumps(manifest).encode("utf-8"),
|
|
3471
3509
|
ContentType="application/json")
|
|
3472
|
-
|
|
3473
|
-
|
|
3510
|
+
out = (f"Indexed {files} files into {len(records)} chunks for {source_url}. "
|
|
3511
|
+
f"Use rag_search(source_url='{source_url}', query=...) to retrieve, then answer the user.")
|
|
3512
|
+
_last_obs["text"] = out
|
|
3513
|
+
return out
|
|
3474
3514
|
except Exception as e: # noqa: BLE001
|
|
3475
|
-
|
|
3515
|
+
msg = f"Error: indexing failed: {type(e).__name__}: {e}"
|
|
3516
|
+
_last_obs["text"] = msg
|
|
3517
|
+
return msg
|
|
3476
3518
|
|
|
3477
3519
|
@tool
|
|
3478
3520
|
def rag_add(source_url: str, text: str) -> str:
|
|
@@ -3483,9 +3525,13 @@ def run_agent_chat(cfg):
|
|
|
3483
3525
|
text: the new information to embed and store.
|
|
3484
3526
|
"""
|
|
3485
3527
|
if not _RAG_AVAILABLE:
|
|
3486
|
-
|
|
3528
|
+
msg = "Error: RAG is not configured."
|
|
3529
|
+
_last_obs["text"] = msg
|
|
3530
|
+
return msg
|
|
3487
3531
|
if not (text or "").strip():
|
|
3488
|
-
|
|
3532
|
+
msg = "Error: no text to add."
|
|
3533
|
+
_last_obs["text"] = msg
|
|
3534
|
+
return msg
|
|
3489
3535
|
_emit({"type": "step", "text": f"RAG source → {source_url}"})
|
|
3490
3536
|
_emit({"type": "step", "text": "Adding info to RAG…"})
|
|
3491
3537
|
try:
|
|
@@ -3499,9 +3545,13 @@ def run_agent_chat(cfg):
|
|
|
3499
3545
|
shard = f"{base}/chunks-add-{int(_t.time())}.jsonl"
|
|
3500
3546
|
body = "\n".join(json.dumps(r, ensure_ascii=False) for r in records).encode("utf-8")
|
|
3501
3547
|
client.put_object(Bucket=bucket, Key=shard, Body=body, ContentType="application/x-ndjson")
|
|
3502
|
-
|
|
3548
|
+
out = f"Added {len(records)} chunk(s) to the knowledge base for {source_url}."
|
|
3549
|
+
_last_obs["text"] = out
|
|
3550
|
+
return out
|
|
3503
3551
|
except Exception as e: # noqa: BLE001
|
|
3504
|
-
|
|
3552
|
+
msg = f"Error: rag_add failed: {type(e).__name__}: {e}"
|
|
3553
|
+
_last_obs["text"] = msg
|
|
3554
|
+
return msg
|
|
3505
3555
|
|
|
3506
3556
|
@tool
|
|
3507
3557
|
def rag_search(source_url: str, query: str, k: int = 0) -> str:
|
|
@@ -3513,7 +3563,9 @@ def run_agent_chat(cfg):
|
|
|
3513
3563
|
k: number of chunks to return (0 = configured default).
|
|
3514
3564
|
"""
|
|
3515
3565
|
if not _RAG_AVAILABLE:
|
|
3516
|
-
|
|
3566
|
+
msg = "Error: RAG is not configured."
|
|
3567
|
+
_last_obs["text"] = msg
|
|
3568
|
+
return msg
|
|
3517
3569
|
_emit({"type": "step", "text": f"RAG source → {source_url}"})
|
|
3518
3570
|
_emit({"type": "step", "text": f"Searching RAG → {query[:60]}"})
|
|
3519
3571
|
try:
|
|
@@ -3521,7 +3573,9 @@ def run_agent_chat(cfg):
|
|
|
3521
3573
|
base = _rag_prefix_for(source_url, prefix)
|
|
3522
3574
|
chunks = _rag_load_chunks(client, bucket, base)
|
|
3523
3575
|
if not chunks:
|
|
3524
|
-
|
|
3576
|
+
msg = f"No knowledge base found for {source_url}. Run rag_index first."
|
|
3577
|
+
_last_obs["text"] = msg
|
|
3578
|
+
return msg
|
|
3525
3579
|
qvec = _embed(rag_embed_base, rag_embed_model, [query])[0]
|
|
3526
3580
|
scored = sorted(
|
|
3527
3581
|
((_cosine(qvec, c.get("embedding") or []), c) for c in chunks),
|
|
@@ -3530,9 +3584,13 @@ def run_agent_chat(cfg):
|
|
|
3530
3584
|
topk = scored[: (k if k and k > 0 else rag_top_k)]
|
|
3531
3585
|
parts = [f"[{c.get('file', '?')}] (score {score:.2f})\n{c.get('text', '')}"
|
|
3532
3586
|
for score, c in topk]
|
|
3533
|
-
|
|
3587
|
+
out = "\n\n---\n\n".join(parts) if parts else "No matches."
|
|
3588
|
+
_last_obs["text"] = out
|
|
3589
|
+
return out
|
|
3534
3590
|
except Exception as e: # noqa: BLE001
|
|
3535
|
-
|
|
3591
|
+
msg = f"Error: rag_search failed: {type(e).__name__}: {e}"
|
|
3592
|
+
_last_obs["text"] = msg
|
|
3593
|
+
return msg
|
|
3536
3594
|
|
|
3537
3595
|
@tool
|
|
3538
3596
|
def grep_repo(pattern: str, path: str = "", glob: str = "") -> str:
|
|
@@ -3573,11 +3631,17 @@ def run_agent_chat(cfg):
|
|
|
3573
3631
|
break
|
|
3574
3632
|
_emit({"type": "step", "text": f"Searching code → {pattern[:60]} ({len(hits)} hits)"})
|
|
3575
3633
|
if not hits:
|
|
3576
|
-
|
|
3634
|
+
msg = f"No matches for {pattern!r} under {root}."
|
|
3635
|
+
_last_obs["text"] = msg
|
|
3636
|
+
return msg
|
|
3577
3637
|
more = "\n…(capped at 100 hits)" if len(hits) >= 100 else ""
|
|
3578
|
-
|
|
3638
|
+
out = f"Matches under {root}:\n" + "\n".join(hits) + more
|
|
3639
|
+
_last_obs["text"] = out
|
|
3640
|
+
return out
|
|
3579
3641
|
except Exception as e: # noqa: BLE001
|
|
3580
|
-
|
|
3642
|
+
msg = f"Error: grep_repo failed: {type(e).__name__}: {e}"
|
|
3643
|
+
_last_obs["text"] = msg
|
|
3644
|
+
return msg
|
|
3581
3645
|
|
|
3582
3646
|
@tool
|
|
3583
3647
|
def read_file_lines(path: str, start_line: int = 1, end_line: int = 0) -> str:
|
|
@@ -3596,9 +3660,13 @@ def run_agent_chat(cfg):
|
|
|
3596
3660
|
e = int(end_line or 0) or len(lines)
|
|
3597
3661
|
e = min(e, len(lines), s + 399)
|
|
3598
3662
|
body = "".join(f"{i}: {lines[i - 1]}" for i in range(s, e + 1))
|
|
3599
|
-
|
|
3663
|
+
out = f"{rp} (lines {s}-{e} of {len(lines)}):\n{body}"
|
|
3664
|
+
_last_obs["text"] = out
|
|
3665
|
+
return out
|
|
3600
3666
|
except Exception as ex: # noqa: BLE001
|
|
3601
|
-
|
|
3667
|
+
msg = f"Error: read_file_lines failed: {type(ex).__name__}: {ex}"
|
|
3668
|
+
_last_obs["text"] = msg
|
|
3669
|
+
return msg
|
|
3602
3670
|
|
|
3603
3671
|
@tool
|
|
3604
3672
|
def edit_lines(path: str, start_line: int, end_line: int, new_content: str) -> str:
|
|
@@ -3614,14 +3682,18 @@ def run_agent_chat(cfg):
|
|
|
3614
3682
|
rp = _ws_write_allowed((path or "").strip())
|
|
3615
3683
|
import os
|
|
3616
3684
|
if not os.path.isfile(rp):
|
|
3617
|
-
|
|
3685
|
+
msg = f"Error: not a file: {path}"
|
|
3686
|
+
_last_obs["text"] = msg
|
|
3687
|
+
return msg
|
|
3618
3688
|
backup = _ws_snapshot(rp)
|
|
3619
3689
|
with open(rp, "r", encoding="utf-8", errors="replace") as fh:
|
|
3620
3690
|
lines = fh.readlines()
|
|
3621
3691
|
s, e = int(start_line), int(end_line)
|
|
3622
3692
|
if not (1 <= s <= e <= len(lines)):
|
|
3623
|
-
|
|
3624
|
-
|
|
3693
|
+
msg = (f"Error: line range {s}-{e} is out of bounds "
|
|
3694
|
+
f"(file has {len(lines)} lines). Re-check with read_file_lines.")
|
|
3695
|
+
_last_obs["text"] = msg
|
|
3696
|
+
return msg
|
|
3625
3697
|
new_lines = new_content.splitlines(keepends=True)
|
|
3626
3698
|
if new_lines and not new_lines[-1].endswith("\n"):
|
|
3627
3699
|
new_lines[-1] += "\n"
|
|
@@ -3632,10 +3704,14 @@ def run_agent_chat(cfg):
|
|
|
3632
3704
|
_emit({"type": "step", "text": f"Edited {os.path.basename(rp)} lines {s}-{e}"})
|
|
3633
3705
|
shown = "".join(f"{i}: {lines[i - 1]}" for i in
|
|
3634
3706
|
range(max(1, s - 2), min(len(lines), s + len(new_lines) + 1) + 1))
|
|
3635
|
-
|
|
3636
|
-
|
|
3707
|
+
out = (f"Replaced lines {s}-{e} of {rp} with {len(new_lines)} line(s). "
|
|
3708
|
+
f"Backup saved. Updated region:\n{shown}")
|
|
3709
|
+
_last_obs["text"] = out
|
|
3710
|
+
return out
|
|
3637
3711
|
except Exception as ex: # noqa: BLE001
|
|
3638
|
-
|
|
3712
|
+
msg = f"Error: edit_lines failed: {type(ex).__name__}: {ex}"
|
|
3713
|
+
_last_obs["text"] = msg
|
|
3714
|
+
return msg
|
|
3639
3715
|
|
|
3640
3716
|
@tool
|
|
3641
3717
|
def edit_file(path: str, old_string: str, new_string: str) -> str:
|
|
@@ -3650,23 +3726,33 @@ def run_agent_chat(cfg):
|
|
|
3650
3726
|
rp = _ws_write_allowed((path or "").strip())
|
|
3651
3727
|
import os
|
|
3652
3728
|
if not os.path.isfile(rp):
|
|
3653
|
-
|
|
3729
|
+
msg = f"Error: not a file: {path}"
|
|
3730
|
+
_last_obs["text"] = msg
|
|
3731
|
+
return msg
|
|
3654
3732
|
with open(rp, "r", encoding="utf-8", errors="replace") as fh:
|
|
3655
3733
|
text = fh.read()
|
|
3656
3734
|
n = text.count(old_string)
|
|
3657
3735
|
if n == 0:
|
|
3658
|
-
|
|
3659
|
-
|
|
3736
|
+
msg = ("Error: old_string not found — it must match EXACTLY "
|
|
3737
|
+
"(check whitespace with read_file_lines, or use edit_lines).")
|
|
3738
|
+
_last_obs["text"] = msg
|
|
3739
|
+
return msg
|
|
3660
3740
|
if n > 1:
|
|
3661
|
-
|
|
3741
|
+
msg = f"Error: old_string occurs {n} times — add surrounding context to make it unique, or use edit_lines."
|
|
3742
|
+
_last_obs["text"] = msg
|
|
3743
|
+
return msg
|
|
3662
3744
|
backup = _ws_snapshot(rp)
|
|
3663
3745
|
with open(rp, "w", encoding="utf-8") as fh:
|
|
3664
3746
|
fh.write(text.replace(old_string, new_string, 1))
|
|
3665
3747
|
_ws_record_change("edit", rp, backup)
|
|
3666
3748
|
_emit({"type": "step", "text": f"Edited {os.path.basename(rp)}"})
|
|
3667
|
-
|
|
3749
|
+
out = f"Replaced 1 occurrence in {rp}. Backup saved."
|
|
3750
|
+
_last_obs["text"] = out
|
|
3751
|
+
return out
|
|
3668
3752
|
except Exception as ex: # noqa: BLE001
|
|
3669
|
-
|
|
3753
|
+
msg = f"Error: edit_file failed: {type(ex).__name__}: {ex}"
|
|
3754
|
+
_last_obs["text"] = msg
|
|
3755
|
+
return msg
|
|
3670
3756
|
|
|
3671
3757
|
@tool
|
|
3672
3758
|
def create_file(path: str, content: str) -> str:
|
|
@@ -3680,15 +3766,21 @@ def run_agent_chat(cfg):
|
|
|
3680
3766
|
rp = _ws_write_allowed((path or "").strip())
|
|
3681
3767
|
import os
|
|
3682
3768
|
if os.path.exists(rp):
|
|
3683
|
-
|
|
3769
|
+
msg = f"Error: {path} already exists — use edit_lines/edit_file to change it."
|
|
3770
|
+
_last_obs["text"] = msg
|
|
3771
|
+
return msg
|
|
3684
3772
|
os.makedirs(os.path.dirname(rp) or ".", exist_ok=True)
|
|
3685
3773
|
with open(rp, "w", encoding="utf-8") as fh:
|
|
3686
3774
|
fh.write(content)
|
|
3687
3775
|
_ws_record_change("create", rp, None)
|
|
3688
3776
|
_emit({"type": "step", "text": f"Created {os.path.basename(rp)}"})
|
|
3689
|
-
|
|
3777
|
+
out = f"Created {rp} ({len(content)} chars)."
|
|
3778
|
+
_last_obs["text"] = out
|
|
3779
|
+
return out
|
|
3690
3780
|
except Exception as ex: # noqa: BLE001
|
|
3691
|
-
|
|
3781
|
+
msg = f"Error: create_file failed: {type(ex).__name__}: {ex}"
|
|
3782
|
+
_last_obs["text"] = msg
|
|
3783
|
+
return msg
|
|
3692
3784
|
|
|
3693
3785
|
@tool
|
|
3694
3786
|
def run_command(command: str, workdir: str = "", timeout_seconds: int = 180) -> str:
|
|
@@ -3706,14 +3798,20 @@ def run_agent_chat(cfg):
|
|
|
3706
3798
|
wd = _rag_read_allowed((workdir or "").strip() or (_WS_ROOTS[0]["path"] if _WS_ROOTS else "."))
|
|
3707
3799
|
w = _ws_for_path(wd)
|
|
3708
3800
|
if w is None or not w.get("allowRun"):
|
|
3709
|
-
|
|
3710
|
-
|
|
3801
|
+
msg = ("Error: running commands is not enabled for this workspace. "
|
|
3802
|
+
"Re-register it with: gonext-local-worker workspace add <path> --allow-run")
|
|
3803
|
+
_last_obs["text"] = msg
|
|
3804
|
+
return msg
|
|
3711
3805
|
argv = shlex.split(command or "")
|
|
3712
3806
|
if not argv:
|
|
3713
|
-
|
|
3807
|
+
msg = "Error: empty command."
|
|
3808
|
+
_last_obs["text"] = msg
|
|
3809
|
+
return msg
|
|
3714
3810
|
if argv[0] not in _WS_RUN_ALLOWED:
|
|
3715
|
-
|
|
3716
|
-
|
|
3811
|
+
msg = (f"Error: '{argv[0]}' is not an allowed runner. Allowed: "
|
|
3812
|
+
+ ", ".join(sorted(_WS_RUN_ALLOWED)))
|
|
3813
|
+
_last_obs["text"] = msg
|
|
3814
|
+
return msg
|
|
3717
3815
|
_emit({"type": "step", "text": f"Running → {command[:70]}"})
|
|
3718
3816
|
t = max(5, min(int(timeout_seconds or 180), 600))
|
|
3719
3817
|
# Scrubbed env: repo test scripts must never see worker secrets.
|
|
@@ -3724,11 +3822,17 @@ def run_agent_chat(cfg):
|
|
|
3724
3822
|
out = _ws_distill_output(proc.stdout or "")
|
|
3725
3823
|
status = "PASSED (exit 0)" if proc.returncode == 0 else f"FAILED (exit {proc.returncode})"
|
|
3726
3824
|
_emit({"type": "step", "text": f"Command {status.split()[0].lower()} → {command[:50]}"})
|
|
3727
|
-
|
|
3825
|
+
result = f"{status}\n{out}" if out.strip() else status
|
|
3826
|
+
_last_obs["text"] = result
|
|
3827
|
+
return result
|
|
3728
3828
|
except subprocess.TimeoutExpired:
|
|
3729
|
-
|
|
3829
|
+
msg = f"Error: command timed out after {timeout_seconds}s."
|
|
3830
|
+
_last_obs["text"] = msg
|
|
3831
|
+
return msg
|
|
3730
3832
|
except Exception as ex: # noqa: BLE001
|
|
3731
|
-
|
|
3833
|
+
msg = f"Error: run_command failed: {type(ex).__name__}: {ex}"
|
|
3834
|
+
_last_obs["text"] = msg
|
|
3835
|
+
return msg
|
|
3732
3836
|
|
|
3733
3837
|
agent_tools = [http_request, web_search, fetch_url, calculate,
|
|
3734
3838
|
get_current_datetime, create_pdf]
|
|
@@ -3825,7 +3929,19 @@ def run_agent_chat(cfg):
|
|
|
3825
3929
|
# a fabricated "final" answer here instead would poison every future turn's
|
|
3826
3930
|
# prompt with raw exception text (this is exactly what caused task #35).
|
|
3827
3931
|
raise e
|
|
3828
|
-
|
|
3932
|
+
# _plain_reply has NO tool/file/HTTP access whatsoever — that's by design (it's a
|
|
3933
|
+
# bare chat completion). Without a note here, its honest "I don't have access to
|
|
3934
|
+
# your workspace/that page/etc." reads to the user as a PERMISSIONS bug, when the
|
|
3935
|
+
# real cause is that the tool-capable agent loop failed upstream (e.g. the coding
|
|
3936
|
+
# model timed out) and we silently swapped in a capability-limited fallback. Make
|
|
3937
|
+
# that swap visible so the user can tell "genuinely no access" apart from
|
|
3938
|
+
# "the real attempt never got to run" (seen live: workspace summarize request →
|
|
3939
|
+
# coding model timed out 3x on a slow/unreachable remote Ollama box → silent
|
|
3940
|
+
# fallback claimed "no access to your workspace").
|
|
3941
|
+
_emit({"type": "final", "text": (
|
|
3942
|
+
f"⚠️ I couldn't finish the full investigation ({_clip(str(e), 120)}) — here's "
|
|
3943
|
+
f"what I can say without it:\n\n{fallback}"
|
|
3944
|
+
)})
|
|
3829
3945
|
|
|
3830
3946
|
|
|
3831
3947
|
def _log(text: str):
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiens.nguyen/gonext-local-worker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.194",
|
|
4
4
|
"description": "Polls GoNext cloud API for async local LLM jobs and runs them against Ollama/OpenAI-compatible servers on this Mac",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|