@widgex/wgx 0.0.0-stage → 0.4.1
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/LICENSE.txt +49 -0
- package/README.md +33 -2
- package/ThirdPartyNotices.txt +4802 -0
- package/package.json +29 -4
- package/python/XACTOR.md +146 -0
- package/python/browser_actor_host.py +456 -0
- package/python/cdp_page.py +363 -0
- package/python/harness_check.py +77 -0
- package/python/requirements.txt +1 -0
- package/python/test_line_endings.py +185 -0
- package/python/xactor/__init__.py +18 -0
- package/python/xactor/__main__.py +226 -0
- package/python/xactor/api.py +882 -0
- package/python/xactor/bridge.py +784 -0
- package/python/xactor/dom.py +117 -0
- package/python/xactor/man/add_card.md +89 -0
- package/python/xactor/man/agent-vs-fork.md +108 -0
- package/python/xactor/man/agent.md +333 -0
- package/python/xactor/man/ask_model.md +110 -0
- package/python/xactor/man/ask_reference.md +56 -0
- package/python/xactor/man/ask_user.md +103 -0
- package/python/xactor/man/attach_app.md +595 -0
- package/python/xactor/man/attach_ide.md +65 -0
- package/python/xactor/man/attach_ide_surface.md +31 -0
- package/python/xactor/man/await_user.md +214 -0
- package/python/xactor/man/bash.md +142 -0
- package/python/xactor/man/browser-driver.md +137 -0
- package/python/xactor/man/browser_activity.md +53 -0
- package/python/xactor/man/browser_attach.md +84 -0
- package/python/xactor/man/browser_close_tab.md +50 -0
- package/python/xactor/man/browser_detach.md +50 -0
- package/python/xactor/man/browser_exec.md +66 -0
- package/python/xactor/man/browser_focus_tab.md +47 -0
- package/python/xactor/man/browser_hybrid.md +47 -0
- package/python/xactor/man/browser_open.md +50 -0
- package/python/xactor/man/browser_open_tab.md +58 -0
- package/python/xactor/man/browser_plumbing.md +107 -0
- package/python/xactor/man/browser_screenshot.md +56 -0
- package/python/xactor/man/browser_snapshot.md +52 -0
- package/python/xactor/man/browser_tabs.md +67 -0
- package/python/xactor/man/bulletin.md +88 -0
- package/python/xactor/man/capture_page.md +54 -0
- package/python/xactor/man/cd.md +64 -0
- package/python/xactor/man/chess_move.md +53 -0
- package/python/xactor/man/children.md +89 -0
- package/python/xactor/man/close_task.md +14 -0
- package/python/xactor/man/compact.md +75 -0
- package/python/xactor/man/drive.md +140 -0
- package/python/xactor/man/drive_space.md +72 -0
- package/python/xactor/man/driving.md +168 -0
- package/python/xactor/man/edit_file.md +112 -0
- package/python/xactor/man/finding.md +194 -0
- package/python/xactor/man/fork_agent.md +45 -0
- package/python/xactor/man/glob.md +83 -0
- package/python/xactor/man/grep.md +115 -0
- package/python/xactor/man/harness.md +165 -0
- package/python/xactor/man/harness_check.md +70 -0
- package/python/xactor/man/harness_format.md +257 -0
- package/python/xactor/man/highlight.md +66 -0
- package/python/xactor/man/instruct.md +111 -0
- package/python/xactor/man/ls.md +63 -0
- package/python/xactor/man/man.md +86 -0
- package/python/xactor/man/merge_wiz.md +40 -0
- package/python/xactor/man/notebook.md +98 -0
- package/python/xactor/man/overview.md +89 -0
- package/python/xactor/man/permissions.md +137 -0
- package/python/xactor/man/phase.md +150 -0
- package/python/xactor/man/powershell.md +74 -0
- package/python/xactor/man/profiles.md +60 -0
- package/python/xactor/man/project_drive.md +49 -0
- package/python/xactor/man/read_bulletin.md +89 -0
- package/python/xactor/man/read_file.md +90 -0
- package/python/xactor/man/relay.md +216 -0
- package/python/xactor/man/reply.md +72 -0
- package/python/xactor/man/reply_message.md +78 -0
- package/python/xactor/man/report_bug.md +118 -0
- package/python/xactor/man/request_answer.md +142 -0
- package/python/xactor/man/request_merge_decisions.md +121 -0
- package/python/xactor/man/request_operator_action.md +152 -0
- package/python/xactor/man/request_review.md +122 -0
- package/python/xactor/man/request_user_action.md +126 -0
- package/python/xactor/man/return_card.md +71 -0
- package/python/xactor/man/run.md +99 -0
- package/python/xactor/man/sandbox.md +144 -0
- package/python/xactor/man/session_fork.md +74 -0
- package/python/xactor/man/session_harness.md +109 -0
- package/python/xactor/man/session_join.md +102 -0
- package/python/xactor/man/session_land.md +73 -0
- package/python/xactor/man/session_nudge.md +73 -0
- package/python/xactor/man/session_rest.md +73 -0
- package/python/xactor/man/session_scope.md +84 -0
- package/python/xactor/man/session_terminate.md +58 -0
- package/python/xactor/man/session_token.md +78 -0
- package/python/xactor/man/sessions.md +93 -0
- package/python/xactor/man/set_harness.md +15 -0
- package/python/xactor/man/spawn_swarm.md +80 -0
- package/python/xactor/man/submit_evidence.md +54 -0
- package/python/xactor/man/swarm_brief.md +30 -0
- package/python/xactor/man/task_status.md +82 -0
- package/python/xactor/man/tasks.md +47 -0
- package/python/xactor/man/todo_write.md +104 -0
- package/python/xactor/man/update_card.md +70 -0
- package/python/xactor/man/use_profile.md +90 -0
- package/python/xactor/man/use_widget.md +82 -0
- package/python/xactor/man/wait_for_task.md +105 -0
- package/python/xactor/man/widget-model.md +284 -0
- package/python/xactor/man/widgets.md +59 -0
- package/python/xactor/man/write_file.md +76 -0
- package/python/xactor/man/writing-a-widget.md +142 -0
- package/python/xactor/man/xylon.md +120 -0
- package/python/xactor/man/xylon_blame.md +80 -0
- package/python/xactor/man/xylon_read.md +131 -0
- package/python/xactor/man/xylon_search.md +125 -0
- package/python/xactor/manual.py +195 -0
- package/python/xactor/parent_watch.py +115 -0
- package/python/xactor/plan_mode.py +350 -0
- package/python/xactor/sandbox.py +856 -0
- package/python/xactor/seals.py +438 -0
- package/python/xactor/shell_hints.py +210 -0
- package/python/xactor/states/__init__.py +13 -0
- package/python/xactor/states/base.py +523 -0
- package/python/xactor/states/registry.py +274 -0
- package/python/xactor/stdlib_api.py +6345 -0
- package/python/xactor/widgets/__init__.py +1 -0
- package/python/xactor/widgets/github_tokens.py +314 -0
- package/python/xactor/widgets/google_aistudio.py +365 -0
- package/settings-schema.json +403 -0
- package/viewer/index.css +160 -0
- package/viewer/index.js +15 -0
- package/wgx.mjs +273753 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# api.ask_model(model, prompt, system=None, temperature=None, max_tokens=None, stop=None, timeout_seconds=None) -> str
|
|
2
|
+
|
|
3
|
+
<!-- group: Sessions -->
|
|
4
|
+
<!-- also: complete, completion, generate, llm, sample, smiles, local-model -->
|
|
5
|
+
|
|
6
|
+
Ask one model one question. No tools, no subagent, no session. Never asks.
|
|
7
|
+
|
|
8
|
+
`api.agent` hands *work* over — the thing on the other end gets your shell, reads
|
|
9
|
+
files, runs code, and decides for itself when it is done. This hands over
|
|
10
|
+
nothing. It sends a prompt to a model and gives you back what it said, as a
|
|
11
|
+
string.
|
|
12
|
+
|
|
13
|
+
## When you need it
|
|
14
|
+
|
|
15
|
+
Some models cannot call tools. Not "would rather not" — were never trained to,
|
|
16
|
+
and will never emit a tool call however you prompt them. A chemical language
|
|
17
|
+
model that answers in SMILES strings, a classifier, a fine-tune that returns one
|
|
18
|
+
line: these cannot participate in an agent loop, because an agent loop is made of
|
|
19
|
+
tool calls.
|
|
20
|
+
|
|
21
|
+
`api.agent` refuses them by name, and so does the session picker and the harness
|
|
22
|
+
config. This is the way in.
|
|
23
|
+
|
|
24
|
+
smiles = await api.ask_model(
|
|
25
|
+
'ollama/smiley:latest',
|
|
26
|
+
'Generate a drug-like molecule with logP below 5 containing a '
|
|
27
|
+
'piperazine ring. Reply with the SMILES string only.',
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
You can ask any model this way, not only the tool-less ones — it is simply a
|
|
31
|
+
cheaper shape when all you want is an answer. But for a tool-less model it is the
|
|
32
|
+
only shape available.
|
|
33
|
+
|
|
34
|
+
## What comes back is data, not work done
|
|
35
|
+
|
|
36
|
+
The string has been checked by nothing. The model that wrote it had no files, no
|
|
37
|
+
shell, and no way to verify its own answer — and it will answer confidently
|
|
38
|
+
whatever you ask it, including about things it has never seen. It does not refuse
|
|
39
|
+
out-of-scope questions; it makes something up.
|
|
40
|
+
|
|
41
|
+
So: parse it, validate it, run it past a real tool, throw away what does not hold
|
|
42
|
+
up. Do not hand one of these a coding task, a file path, or a question about this
|
|
43
|
+
repository.
|
|
44
|
+
|
|
45
|
+
## Several answers at once
|
|
46
|
+
|
|
47
|
+
There is deliberately no `n`. The OpenAI parameter of that name is not
|
|
48
|
+
implemented by the local runtimes this call exists for, so passing it would
|
|
49
|
+
quietly return one sample while looking like it returned eight.
|
|
50
|
+
|
|
51
|
+
Ask several times instead. Every api call on this shell is a coroutine, and the
|
|
52
|
+
bridge multiplexes them, so this really is concurrent:
|
|
53
|
+
|
|
54
|
+
import asyncio
|
|
55
|
+
|
|
56
|
+
brief = 'Generate a drug-like molecule with a piperazine ring. SMILES only.'
|
|
57
|
+
candidates = await asyncio.gather(*[
|
|
58
|
+
api.ask_model('ollama/smiley:latest', brief, temperature=1.0)
|
|
59
|
+
for _ in range(8)
|
|
60
|
+
])
|
|
61
|
+
|
|
62
|
+
Raise `temperature` when you do this. At the model's default you may get the same
|
|
63
|
+
answer eight times, which is not a bug and is not useful either.
|
|
64
|
+
|
|
65
|
+
## Arguments
|
|
66
|
+
|
|
67
|
+
| | |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `model` | `'provider/model'`, or a bare id when only one provider offers it. Unknown ids are refused with the list of what is available. |
|
|
70
|
+
| `prompt` | The whole question. It sees nothing of this conversation. |
|
|
71
|
+
| `system` | A system prompt, if the model expects one. Omitted sends **none at all**, not an empty one — for a fine-tune trained on one exact prompt shape those are different prompts. |
|
|
72
|
+
| `temperature` | 0 to 2. Omit for the model's own default. |
|
|
73
|
+
| `max_tokens` | Cap on the answer. See below. |
|
|
74
|
+
| `stop` | Up to four stop sequences. A bare string is fine. |
|
|
75
|
+
| `timeout_seconds` | Default 300. |
|
|
76
|
+
|
|
77
|
+
## Hitting `max_tokens` raises
|
|
78
|
+
|
|
79
|
+
A completion that hit its cap does not come back as a short answer — it raises.
|
|
80
|
+
|
|
81
|
+
This is deliberate and it is about the models this call serves. A SMILES string
|
|
82
|
+
truncated mid-token still parses and still looks like a molecule. Returning it
|
|
83
|
+
would let you file something chemically meaningless as a result, and nothing
|
|
84
|
+
downstream would catch it. If you get `AskModelTruncated`, raise `max_tokens` and
|
|
85
|
+
ask again.
|
|
86
|
+
|
|
87
|
+
## The first call is slow
|
|
88
|
+
|
|
89
|
+
A local model that is not already resident spends thirty seconds or more loading
|
|
90
|
+
before it says its first word. That is why the default timeout is 300 seconds and
|
|
91
|
+
not 30. Subsequent calls are fast until the server unloads it again.
|
|
92
|
+
|
|
93
|
+
`AskModelTimeout` says this in its message, so you can tell it apart from a model
|
|
94
|
+
that is genuinely stuck.
|
|
95
|
+
|
|
96
|
+
## What it refuses
|
|
97
|
+
|
|
98
|
+
- **A model served by a CLI that runs its own agent loop** — Claude Code, Gemini,
|
|
99
|
+
Antigravity. They expose no completion endpoint. Refused by name rather than
|
|
100
|
+
quietly answered by a different model, because a fabricated answer attributed
|
|
101
|
+
to a model that never saw the prompt is worse than an error.
|
|
102
|
+
- **An unknown or disabled model** — with the list of what is available, and the
|
|
103
|
+
user's own notes on what each is for.
|
|
104
|
+
|
|
105
|
+
## Related
|
|
106
|
+
|
|
107
|
+
- [[agent]] — hand over work, to a model that can act
|
|
108
|
+
- [[children]] — what you have already started
|
|
109
|
+
|
|
110
|
+
Never gated. It starts nothing, outlives nothing, and touches no file.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# api.ask_reference(ref, question, timeout=300) -> dict
|
|
2
|
+
<!-- group: Sessions -->
|
|
3
|
+
|
|
4
|
+
Ask an earlier conversation that the person attached to this chat.
|
|
5
|
+
|
|
6
|
+
A `<reference>` block at the top of a prompt is a summary and a changelog of
|
|
7
|
+
work done somewhere else. When that is not enough — you need to know why
|
|
8
|
+
something was decided, what was tried and dropped, or what a file looked like
|
|
9
|
+
at the time — ask it.
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
r = await api.ask_reference(
|
|
13
|
+
"Widgex/Widgex/Browser-Blocked",
|
|
14
|
+
"Did the fix change input coordinates, or only the screencast metadata?",
|
|
15
|
+
)
|
|
16
|
+
print(r["answer"])
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## What happens
|
|
20
|
+
|
|
21
|
+
The first question starts a **fork** of that conversation: a new agent, filed
|
|
22
|
+
under your task, that begins with the summary and changelog and reads the old
|
|
23
|
+
transcript as it needs to. Every later question for the same reference goes to
|
|
24
|
+
that same fork, so it remembers what you already asked. The person can open it
|
|
25
|
+
and read the exchange.
|
|
26
|
+
|
|
27
|
+
## Options
|
|
28
|
+
|
|
29
|
+
- **`ref`** — the `id` of the reference block. Only a reference attached to
|
|
30
|
+
this conversation can be asked; anything else is refused, and the error lists
|
|
31
|
+
what is attached.
|
|
32
|
+
- **`question`** — one question, specific. The fork quotes the passage an
|
|
33
|
+
answer rests on when you would need to check it; ask for that when it matters.
|
|
34
|
+
- **`timeout`** — seconds to wait. Raise the exec's own `timeout_seconds` past
|
|
35
|
+
it.
|
|
36
|
+
|
|
37
|
+
## Returns
|
|
38
|
+
|
|
39
|
+
`{answer, session, node, status, error?}`
|
|
40
|
+
|
|
41
|
+
- `status: 'done'` — `answer` is the reply.
|
|
42
|
+
- `status: 'timeout'` — no reply yet, `answer` is empty, and the fork is still
|
|
43
|
+
working. It was not stopped: call again to wait for it.
|
|
44
|
+
- `status: 'error'` — the fork's turn failed; `error` says how.
|
|
45
|
+
|
|
46
|
+
Raises when the reference is not attached here, or when the conversation it
|
|
47
|
+
points at is no longer in the store.
|
|
48
|
+
|
|
49
|
+
## Do not
|
|
50
|
+
|
|
51
|
+
- **Do not ask what the block already says.** A fork is a real agent and costs
|
|
52
|
+
like one; the summary and changelog are free.
|
|
53
|
+
- **Do not treat an answer as a fact about this working copy.** It describes
|
|
54
|
+
another one. Check any file, function or commit it names before acting on it.
|
|
55
|
+
- **Do not ask it to make changes.** It is told not to, and it is standing in
|
|
56
|
+
your working copy.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# api.ask_user(question, options, header=None, multi_select=False) -> str
|
|
2
|
+
<!-- group: Talking to the user -->
|
|
3
|
+
|
|
4
|
+
Ask a multiple-choice question and block for the answer. Not gated.
|
|
5
|
+
|
|
6
|
+
**The answer is arbitrary text.** The card always lets the user type something
|
|
7
|
+
of their own instead of picking, so an option label is a suggestion, not a
|
|
8
|
+
constraint on what comes back.
|
|
9
|
+
|
|
10
|
+
## Options
|
|
11
|
+
|
|
12
|
+
- **`question`** — the question, as a full sentence. This string is also the
|
|
13
|
+
key the answer is looked up under, so it must be non-empty.
|
|
14
|
+
- **`options`** — 2 to 4 choices. Either plain strings, or dicts of
|
|
15
|
+
`{"label": str, "description": str}` where the description explains the
|
|
16
|
+
trade-off. **Put your recommendation first**, and say so in its label.
|
|
17
|
+
- **`header`** — a short category chip on the card, ~12 characters:
|
|
18
|
+
`"Auth method"`, `"Library"`. Defaults to `"Question"`.
|
|
19
|
+
- **`multi_select`** — allow more than one choice. The answer then comes back
|
|
20
|
+
as the chosen labels joined by `", "`.
|
|
21
|
+
|
|
22
|
+
At least two options are required; a one-option question is rejected with a
|
|
23
|
+
`ValueError` before anything reaches the user.
|
|
24
|
+
|
|
25
|
+
## Returns
|
|
26
|
+
|
|
27
|
+
A `str`: the label the user chose, the labels they chose joined by `", "`, or
|
|
28
|
+
whatever they typed instead.
|
|
29
|
+
|
|
30
|
+
Raises `RuntimeError` if they dismissed the card without answering. That aborts
|
|
31
|
+
the exec, so do not put irreplaceable work after the call in the same block.
|
|
32
|
+
|
|
33
|
+
## Examples
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
answer = await api.ask_user(
|
|
37
|
+
"Which database should the new service use?",
|
|
38
|
+
[
|
|
39
|
+
{"label": "Postgres (Recommended)",
|
|
40
|
+
"description": "Already used by the billing service; migrations exist."},
|
|
41
|
+
{"label": "SQLite",
|
|
42
|
+
"description": "No server to run, but no concurrent writers."},
|
|
43
|
+
],
|
|
44
|
+
header="Database",
|
|
45
|
+
)
|
|
46
|
+
return answer
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Handling the answer as text, which is what it is:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
answer = await api.ask_user("Ship it now, or wait for review?",
|
|
53
|
+
["Ship now", "Wait for review"])
|
|
54
|
+
if "ship" in answer.lower():
|
|
55
|
+
...
|
|
56
|
+
else:
|
|
57
|
+
# covers "Wait for review" *and* "wait until Thursday please"
|
|
58
|
+
...
|
|
59
|
+
return answer
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Multi-select:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
picks = await api.ask_user(
|
|
66
|
+
"Which checks should run in CI?",
|
|
67
|
+
["Unit tests", "Type check", "Lint", "Bundle size"],
|
|
68
|
+
header="CI checks", multi_select=True,
|
|
69
|
+
)
|
|
70
|
+
return [p.strip() for p in picks.split(",")]
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Getting it wrong
|
|
74
|
+
|
|
75
|
+
**Comparing against your own labels with `==`.**
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
# Wrong — silently takes the else branch when the user typed anything.
|
|
79
|
+
if answer == "Postgres (Recommended)":
|
|
80
|
+
...
|
|
81
|
+
|
|
82
|
+
# Right — the answer may be free text, so read it rather than match it.
|
|
83
|
+
if "postgres" in answer.lower():
|
|
84
|
+
...
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Asking for approval of a plan you are about to execute.** Permission prompts
|
|
88
|
+
already cover the gated calls. Use `ask_user` for a decision that changes
|
|
89
|
+
*what* you build, not to seek reassurance.
|
|
90
|
+
|
|
91
|
+
**Asking what you could find out.** If the answer is in the repo, read the
|
|
92
|
+
repo. A question the user has to answer costs them attention you can only spend
|
|
93
|
+
once.
|
|
94
|
+
|
|
95
|
+
**Options that are not mutually exclusive, without `multi_select=True`.** The
|
|
96
|
+
user then has to type, and you lose the structure you asked for.
|
|
97
|
+
|
|
98
|
+
**Losing work to a dismissal.** The `RuntimeError` kills the whole exec. Ask
|
|
99
|
+
first, act after — ideally in the next exec.
|
|
100
|
+
|
|
101
|
+
## See also
|
|
102
|
+
|
|
103
|
+
`request_user_action`, `todo_write`, `permissions`
|