npcsh 1.1.21__py3-none-any.whl → 1.1.23__py3-none-any.whl
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.
- npcsh/_state.py +282 -125
- npcsh/benchmark/npcsh_agent.py +77 -232
- npcsh/benchmark/templates/install-npcsh.sh.j2 +12 -4
- npcsh/config.py +5 -2
- npcsh/mcp_server.py +9 -1
- npcsh/npc_team/alicanto.npc +8 -6
- npcsh/npc_team/corca.npc +5 -12
- npcsh/npc_team/frederic.npc +6 -9
- npcsh/npc_team/guac.npc +4 -4
- npcsh/npc_team/jinxs/lib/core/delegate.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/edit_file.jinx +84 -62
- npcsh/npc_team/jinxs/lib/core/sh.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/skill.jinx +59 -0
- npcsh/npc_team/jinxs/lib/utils/help.jinx +194 -10
- npcsh/npc_team/jinxs/lib/utils/init.jinx +528 -37
- npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +0 -1
- npcsh/npc_team/jinxs/lib/utils/serve.jinx +938 -21
- npcsh/npc_team/jinxs/modes/alicanto.jinx +102 -41
- npcsh/npc_team/jinxs/modes/build.jinx +378 -0
- npcsh-1.1.21.data/data/npcsh/npc_team/config_tui.jinx → npcsh/npc_team/jinxs/modes/config.jinx +1 -1
- npcsh/npc_team/jinxs/modes/convene.jinx +670 -0
- npcsh/npc_team/jinxs/modes/corca.jinx +777 -387
- npcsh/npc_team/jinxs/modes/crond.jinx +818 -0
- npcsh/npc_team/jinxs/modes/kg.jinx +69 -2
- npcsh/npc_team/jinxs/modes/plonk.jinx +86 -15
- npcsh/npc_team/jinxs/modes/roll.jinx +368 -55
- npcsh/npc_team/jinxs/modes/skills.jinx +621 -0
- npcsh/npc_team/jinxs/modes/yap.jinx +1092 -177
- npcsh/npc_team/jinxs/skills/code-review/SKILL.md +45 -0
- npcsh/npc_team/jinxs/skills/debugging/SKILL.md +44 -0
- npcsh/npc_team/jinxs/skills/git-workflow.jinx +44 -0
- npcsh/npc_team/kadiefa.npc +6 -6
- npcsh/npc_team/npcsh.ctx +16 -0
- npcsh/npc_team/plonk.npc +5 -9
- npcsh/npc_team/sibiji.npc +15 -7
- npcsh/npcsh.py +1 -0
- npcsh/routes.py +0 -4
- npcsh/yap.py +22 -4
- npcsh-1.1.23.data/data/npcsh/npc_team/SKILL.md +44 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/alicanto.jinx +102 -41
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/alicanto.npc +8 -6
- npcsh-1.1.23.data/data/npcsh/npc_team/build.jinx +378 -0
- npcsh/npc_team/jinxs/modes/config_tui.jinx → npcsh-1.1.23.data/data/npcsh/npc_team/config.jinx +1 -1
- npcsh-1.1.23.data/data/npcsh/npc_team/convene.jinx +670 -0
- npcsh-1.1.23.data/data/npcsh/npc_team/corca.jinx +820 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca.npc +5 -12
- npcsh-1.1.23.data/data/npcsh/npc_team/crond.jinx +818 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/delegate.jinx +1 -1
- npcsh-1.1.23.data/data/npcsh/npc_team/edit_file.jinx +119 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/frederic.npc +6 -9
- npcsh-1.1.23.data/data/npcsh/npc_team/git-workflow.jinx +44 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/guac.npc +4 -4
- npcsh-1.1.23.data/data/npcsh/npc_team/help.jinx +236 -0
- npcsh-1.1.23.data/data/npcsh/npc_team/init.jinx +532 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/jinxs.jinx +0 -1
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/kadiefa.npc +6 -6
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/kg.jinx +69 -2
- npcsh-1.1.23.data/data/npcsh/npc_team/npcsh.ctx +34 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonk.jinx +86 -15
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonk.npc +5 -9
- npcsh-1.1.23.data/data/npcsh/npc_team/roll.jinx +378 -0
- npcsh-1.1.23.data/data/npcsh/npc_team/serve.jinx +943 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sh.jinx +1 -1
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sibiji.npc +15 -7
- npcsh-1.1.23.data/data/npcsh/npc_team/skill.jinx +59 -0
- npcsh-1.1.23.data/data/npcsh/npc_team/skills.jinx +621 -0
- npcsh-1.1.23.data/data/npcsh/npc_team/yap.jinx +1190 -0
- {npcsh-1.1.21.dist-info → npcsh-1.1.23.dist-info}/METADATA +404 -278
- npcsh-1.1.23.dist-info/RECORD +216 -0
- npcsh/npc_team/jinxs/incognide/add_tab.jinx +0 -11
- npcsh/npc_team/jinxs/incognide/close_pane.jinx +0 -9
- npcsh/npc_team/jinxs/incognide/close_tab.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/confirm.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/focus_pane.jinx +0 -9
- npcsh/npc_team/jinxs/incognide/list_panes.jinx +0 -8
- npcsh/npc_team/jinxs/incognide/navigate.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/notify.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/open_pane.jinx +0 -13
- npcsh/npc_team/jinxs/incognide/read_pane.jinx +0 -9
- npcsh/npc_team/jinxs/incognide/run_terminal.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/send_message.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/split_pane.jinx +0 -12
- npcsh/npc_team/jinxs/incognide/switch_npc.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/switch_tab.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/write_file.jinx +0 -11
- npcsh/npc_team/jinxs/incognide/zen_mode.jinx +0 -9
- npcsh/npc_team/jinxs/lib/core/convene.jinx +0 -232
- npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +0 -429
- npcsh/npc_team/jinxs/lib/core/search.jinx +0 -54
- npcsh/npc_team/jinxs/lib/utils/build.jinx +0 -65
- npcsh-1.1.21.data/data/npcsh/npc_team/add_tab.jinx +0 -11
- npcsh-1.1.21.data/data/npcsh/npc_team/build.jinx +0 -65
- npcsh-1.1.21.data/data/npcsh/npc_team/close_pane.jinx +0 -9
- npcsh-1.1.21.data/data/npcsh/npc_team/close_tab.jinx +0 -10
- npcsh-1.1.21.data/data/npcsh/npc_team/confirm.jinx +0 -10
- npcsh-1.1.21.data/data/npcsh/npc_team/convene.jinx +0 -232
- npcsh-1.1.21.data/data/npcsh/npc_team/corca.jinx +0 -430
- npcsh-1.1.21.data/data/npcsh/npc_team/edit_file.jinx +0 -97
- npcsh-1.1.21.data/data/npcsh/npc_team/focus_pane.jinx +0 -9
- npcsh-1.1.21.data/data/npcsh/npc_team/help.jinx +0 -52
- npcsh-1.1.21.data/data/npcsh/npc_team/init.jinx +0 -41
- npcsh-1.1.21.data/data/npcsh/npc_team/kg_search.jinx +0 -429
- npcsh-1.1.21.data/data/npcsh/npc_team/list_panes.jinx +0 -8
- npcsh-1.1.21.data/data/npcsh/npc_team/navigate.jinx +0 -10
- npcsh-1.1.21.data/data/npcsh/npc_team/notify.jinx +0 -10
- npcsh-1.1.21.data/data/npcsh/npc_team/npcsh.ctx +0 -18
- npcsh-1.1.21.data/data/npcsh/npc_team/open_pane.jinx +0 -13
- npcsh-1.1.21.data/data/npcsh/npc_team/read_pane.jinx +0 -9
- npcsh-1.1.21.data/data/npcsh/npc_team/roll.jinx +0 -65
- npcsh-1.1.21.data/data/npcsh/npc_team/run_terminal.jinx +0 -10
- npcsh-1.1.21.data/data/npcsh/npc_team/search.jinx +0 -54
- npcsh-1.1.21.data/data/npcsh/npc_team/send_message.jinx +0 -10
- npcsh-1.1.21.data/data/npcsh/npc_team/serve.jinx +0 -26
- npcsh-1.1.21.data/data/npcsh/npc_team/split_pane.jinx +0 -12
- npcsh-1.1.21.data/data/npcsh/npc_team/switch_npc.jinx +0 -10
- npcsh-1.1.21.data/data/npcsh/npc_team/switch_tab.jinx +0 -10
- npcsh-1.1.21.data/data/npcsh/npc_team/write_file.jinx +0 -11
- npcsh-1.1.21.data/data/npcsh/npc_team/yap.jinx +0 -275
- npcsh-1.1.21.data/data/npcsh/npc_team/zen_mode.jinx +0 -9
- npcsh-1.1.21.dist-info/RECORD +0 -243
- /npcsh/npc_team/jinxs/lib/{core → utils}/chat.jinx +0 -0
- /npcsh/npc_team/jinxs/lib/{core → utils}/cmd.jinx +0 -0
- /npcsh/npc_team/jinxs/{incognide → lib/utils}/incognide.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/alicanto.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/arxiv.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/benchmark.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/chat.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/click.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/cmd.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/compile.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/compress.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca_example.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/db_search.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/file_search.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/frederic4.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/git.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/guac.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/guac.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/incognide.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/kadiefa.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/key_press.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/load_file.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/memories.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/models.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/nql.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/ots.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/papers.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/paste.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonk.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonkjr.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/pti.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/python.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/reattach.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sample.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/set.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/setup.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/shh.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sibiji.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sleep.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/spool.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/spool.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sql.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/switch.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/switches.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sync.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/team.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/trigger.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/type_text.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/usage.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/verbose.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/vixynt.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/wait.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/wander.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/web_search.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/yap.png +0 -0
- {npcsh-1.1.21.dist-info → npcsh-1.1.23.dist-info}/WHEEL +0 -0
- {npcsh-1.1.21.dist-info → npcsh-1.1.23.dist-info}/entry_points.txt +0 -0
- {npcsh-1.1.21.dist-info → npcsh-1.1.23.dist-info}/licenses/LICENSE +0 -0
- {npcsh-1.1.21.dist-info → npcsh-1.1.23.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
jinx_name: kg
|
|
2
2
|
description: Interactive knowledge graph browser - explore facts, concepts, and links
|
|
3
3
|
interactive: true
|
|
4
|
-
inputs:
|
|
4
|
+
inputs:
|
|
5
|
+
- action: ""
|
|
6
|
+
- dream: false
|
|
7
|
+
- backfill: false
|
|
8
|
+
- ops: ""
|
|
5
9
|
steps:
|
|
6
10
|
- name: kg_browser
|
|
7
11
|
engine: python
|
|
@@ -12,7 +16,70 @@ steps:
|
|
|
12
16
|
import termios
|
|
13
17
|
import select
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
_kg_action = (context.get('action') or '').strip().lower()
|
|
20
|
+
|
|
21
|
+
if _kg_action in ('sleep', 'evolve', 'dream'):
|
|
22
|
+
# Route to KG evolution operations
|
|
23
|
+
import traceback
|
|
24
|
+
from npcpy.memory.command_history import CommandHistory, load_kg_from_db, save_kg_to_db
|
|
25
|
+
from npcpy.memory.knowledge_graph import kg_sleep_process, kg_dream_process, kg_backfill_from_memories
|
|
26
|
+
|
|
27
|
+
_npc = context.get('npc')
|
|
28
|
+
_team = context.get('team')
|
|
29
|
+
_msgs = context.get('messages', [])
|
|
30
|
+
_do_dream = _kg_action == 'dream' or str(context.get('dream', '')).lower() in ('true', '1', 'yes')
|
|
31
|
+
_do_backfill = str(context.get('backfill', '')).lower() in ('true', '1', 'yes')
|
|
32
|
+
_ops_str = context.get('ops', '')
|
|
33
|
+
_ops_config = [op.strip() for op in _ops_str.split(',') if op.strip()] if _ops_str else None
|
|
34
|
+
|
|
35
|
+
_model = (_npc.model if _npc and hasattr(_npc, 'model') else None) or (state.chat_model if state else 'llama3.2')
|
|
36
|
+
_provider = (_npc.provider if _npc and hasattr(_npc, 'provider') else None) or (state.chat_provider if state else 'ollama')
|
|
37
|
+
|
|
38
|
+
_team_name = _team.name if _team else '__none__'
|
|
39
|
+
_npc_name = _npc.name if _npc else '__none__'
|
|
40
|
+
_cur_path = os.getcwd()
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
_db_path = os.getenv('NPCSH_DB_PATH', os.path.expanduser('~/npcsh_history.db'))
|
|
44
|
+
_ch = CommandHistory(_db_path)
|
|
45
|
+
_eng = _ch.engine
|
|
46
|
+
|
|
47
|
+
_result = ''
|
|
48
|
+
if _do_backfill:
|
|
49
|
+
print('Backfilling from approved memories...')
|
|
50
|
+
_stats = kg_backfill_from_memories(_eng, model=_model, provider=_provider, npc=_npc, get_concepts=True, dry_run=False)
|
|
51
|
+
_result += f"Backfill: +{_stats['facts_after'] - _stats['facts_before']} facts, +{_stats['concepts_after'] - _stats['concepts_before']} concepts\n"
|
|
52
|
+
|
|
53
|
+
_kg = load_kg_from_db(_eng, _team_name, _npc_name, _cur_path)
|
|
54
|
+
if not _kg or not _kg.get('facts'):
|
|
55
|
+
context['output'] = _result + 'Knowledge graph is empty. Use /kg backfill=true or have conversations first.'
|
|
56
|
+
context['messages'] = _msgs
|
|
57
|
+
_ch.close()
|
|
58
|
+
exit()
|
|
59
|
+
|
|
60
|
+
_f0 = len(_kg.get('facts', []))
|
|
61
|
+
_c0 = len(_kg.get('concepts', []))
|
|
62
|
+
_label = 'Sleep'
|
|
63
|
+
|
|
64
|
+
_kg, _ = kg_sleep_process(existing_kg=_kg, model=_model, provider=_provider, npc=_npc, operations_config=_ops_config)
|
|
65
|
+
|
|
66
|
+
if _do_dream:
|
|
67
|
+
_label += ' & Dream'
|
|
68
|
+
_kg, _ = kg_dream_process(existing_kg=_kg, model=_model, provider=_provider, npc=_npc)
|
|
69
|
+
|
|
70
|
+
save_kg_to_db(_eng, _kg, _team_name, _npc_name, _cur_path)
|
|
71
|
+
_f1 = len(_kg.get('facts', []))
|
|
72
|
+
_c1 = len(_kg.get('concepts', []))
|
|
73
|
+
_result += f"{_label} complete. Facts: {_f0} -> {_f1} ({_f1-_f0:+}), Concepts: {_c0} -> {_c1} ({_c1-_c0:+})"
|
|
74
|
+
context['output'] = _result
|
|
75
|
+
context['messages'] = _msgs
|
|
76
|
+
_ch.close()
|
|
77
|
+
except Exception as e:
|
|
78
|
+
traceback.print_exc()
|
|
79
|
+
context['output'] = f'KG evolution error: {e}'
|
|
80
|
+
context['messages'] = _msgs
|
|
81
|
+
|
|
82
|
+
elif not sys.stdin.isatty():
|
|
16
83
|
context['output'] = "KG browser requires an interactive terminal."
|
|
17
84
|
|
|
18
85
|
else:
|
|
@@ -24,6 +24,25 @@ steps:
|
|
|
24
24
|
|
|
25
25
|
from npcpy.llm_funcs import get_llm_response
|
|
26
26
|
|
|
27
|
+
# Helper to log jinx executions to DB
|
|
28
|
+
def _log_jinx(trigger_id, npc_name, inputs, output, status="success", error_msg=None):
|
|
29
|
+
try:
|
|
30
|
+
if state and hasattr(state, 'command_history') and state.command_history is not None and hasattr(state.command_history, 'save_jinx_execution'):
|
|
31
|
+
_conv_id = getattr(state, 'conversation_id', None) or ''
|
|
32
|
+
state.command_history.save_jinx_execution(
|
|
33
|
+
triggering_message_id=f"{_conv_id}-{trigger_id}",
|
|
34
|
+
conversation_id=_conv_id,
|
|
35
|
+
npc_name=npc_name,
|
|
36
|
+
jinx_name="plonk",
|
|
37
|
+
jinx_inputs=inputs,
|
|
38
|
+
jinx_output=str(output) if output else "",
|
|
39
|
+
status=status,
|
|
40
|
+
team_name=state.team.name if state and hasattr(state, 'team') and state.team else None,
|
|
41
|
+
error_message=error_msg,
|
|
42
|
+
)
|
|
43
|
+
except Exception:
|
|
44
|
+
pass
|
|
45
|
+
|
|
27
46
|
try:
|
|
28
47
|
from npcpy.data.image import capture_screenshot
|
|
29
48
|
from npcpy.work.desktop import perform_action
|
|
@@ -123,7 +142,7 @@ steps:
|
|
|
123
142
|
render_screen()
|
|
124
143
|
|
|
125
144
|
try:
|
|
126
|
-
ss = capture_screenshot()
|
|
145
|
+
ss = capture_screenshot(full=True)
|
|
127
146
|
if not ss or 'file_path' not in ss:
|
|
128
147
|
task['actions'].append({"action": "fail", "reason": "Screenshot failed"})
|
|
129
148
|
task['status'] = 'failed'
|
|
@@ -136,23 +155,43 @@ steps:
|
|
|
136
155
|
history_context = ""
|
|
137
156
|
if task['actions']:
|
|
138
157
|
history_context = "\nPrevious actions:\n"
|
|
139
|
-
for i, act in enumerate(task['actions'][-
|
|
158
|
+
for i, act in enumerate(task['actions'][-8:], 1):
|
|
140
159
|
history_context += " " + str(i) + ". " + act.get('action', '?')
|
|
141
160
|
if act.get('x'):
|
|
142
161
|
history_context += " at (" + str(act.get('x', '?')) + ", " + str(act.get('y', '?')) + ")"
|
|
162
|
+
if act.get('text'):
|
|
163
|
+
history_context += ' "' + act.get('text', '')[:50] + '"'
|
|
164
|
+
if act.get('command'):
|
|
165
|
+
history_context += ' [' + act.get('command', '')[:30] + ']'
|
|
143
166
|
history_context += " - " + act.get('reason', '') + "\n"
|
|
167
|
+
# Detect repetition: if last 3 actions are the same type, warn
|
|
168
|
+
recent = task['actions'][-3:]
|
|
169
|
+
if len(recent) == 3 and all(a.get('action') == recent[0].get('action') for a in recent):
|
|
170
|
+
history_context += "\n*** WARNING: You have repeated '" + recent[0].get('action', '?') + "' 3 times. "
|
|
171
|
+
history_context += "This is NOT working. You MUST try a completely different approach. ***\n"
|
|
144
172
|
|
|
145
173
|
prompt = "You are a GUI automation assistant. Analyze this screenshot and determine the next action.\n\n"
|
|
174
|
+
prompt += "CRITICAL RULES:\n"
|
|
175
|
+
prompt += "1. VERIFY: Check the screenshot BEFORE acting. Does it show the result of your last action? If not, wait.\n"
|
|
176
|
+
prompt += "2. NO REPEATS: If an action failed or had no effect, try a DIFFERENT approach. Never repeat the same action.\n"
|
|
177
|
+
prompt += "3. FULL TEXT: Always type the COMPLETE text in a single 'type' action. Never truncate or split text.\n"
|
|
178
|
+
prompt += "4. FOCUS FIRST: Before typing, make sure the target field is focused (click it or use keyboard shortcut).\n\n"
|
|
179
|
+
prompt += "KEYBOARD SHORTCUTS (use 'key' action with these):\n"
|
|
180
|
+
prompt += "- ctrl+l or F6: Focus browser address/search bar\n"
|
|
181
|
+
prompt += "- ctrl+a: Select all text in current field\n"
|
|
182
|
+
prompt += "- ctrl+t: New browser tab\n"
|
|
183
|
+
prompt += "- tab/shift+tab: Move between form fields\n"
|
|
184
|
+
prompt += "- escape: Close popups, cancel autocomplete\n\n"
|
|
146
185
|
prompt += "TASK: " + task['text'] + "\n"
|
|
147
186
|
prompt += history_context + "\n"
|
|
148
187
|
prompt += "Available actions:\n"
|
|
149
|
-
prompt += "- click: Click at x,y
|
|
150
|
-
prompt += "- type: Type text (
|
|
151
|
-
prompt += "- key: Press key
|
|
152
|
-
prompt += "- launch: Launch application
|
|
153
|
-
prompt += "- wait: Wait
|
|
154
|
-
prompt += "- done: Task completed
|
|
155
|
-
prompt += "- fail: Task
|
|
188
|
+
prompt += "- click: Click at x,y (0-100 % of screen). Fields: x, y\n"
|
|
189
|
+
prompt += "- type: Type text into focused field. Fields: text (MUST be complete text)\n"
|
|
190
|
+
prompt += "- key: Press key(s). Fields: text (e.g. 'enter', 'ctrl+l', 'tab', 'escape')\n"
|
|
191
|
+
prompt += "- launch: Launch application. Fields: command (e.g. " + app_examples + ")\n"
|
|
192
|
+
prompt += "- wait: Wait seconds. Fields: duration\n"
|
|
193
|
+
prompt += "- done: Task completed\n"
|
|
194
|
+
prompt += "- fail: Task impossible\n\n"
|
|
156
195
|
prompt += "Respond with JSON, e.g.: " + json_schema_example
|
|
157
196
|
|
|
158
197
|
ui.status = "Thinking... (iter " + str(ui.iteration) + "/" + str(ui.max_iter) + ")"
|
|
@@ -173,21 +212,38 @@ steps:
|
|
|
173
212
|
action_response = json.loads(action_response)
|
|
174
213
|
except:
|
|
175
214
|
task['actions'].append({"action": "error", "reason": "Invalid JSON from model"})
|
|
215
|
+
_log_jinx(f"plonk-task-{ui.current_task}-iter-{ui.iteration}",
|
|
216
|
+
npc.name if npc and hasattr(npc, 'name') else "plonk",
|
|
217
|
+
{"task": task['text'], "iteration": ui.iteration, "type": "analysis"},
|
|
218
|
+
str(resp.get('response', '')), status="error", error_msg="Invalid JSON from model")
|
|
176
219
|
ui.status = "Bad response, retrying..."
|
|
177
220
|
return
|
|
178
221
|
|
|
179
222
|
action = action_response.get('action', 'fail')
|
|
180
223
|
reason = action_response.get('reason', '')
|
|
181
224
|
|
|
225
|
+
_npc_name = npc.name if npc and hasattr(npc, 'name') else "plonk"
|
|
226
|
+
_log_jinx(f"plonk-task-{ui.current_task}-iter-{ui.iteration}",
|
|
227
|
+
_npc_name,
|
|
228
|
+
{"task": task['text'], "iteration": ui.iteration, "screenshot": screenshot_path,
|
|
229
|
+
"type": "analysis"},
|
|
230
|
+
json.dumps(action_response))
|
|
231
|
+
|
|
182
232
|
if action == 'done':
|
|
183
233
|
task['status'] = 'done'
|
|
184
234
|
task['actions'].append({"action": "done", "reason": reason})
|
|
235
|
+
_log_jinx(f"plonk-task-{ui.current_task}-done", _npc_name,
|
|
236
|
+
{"task": task['text'], "type": "task_complete", "total_actions": len(task['actions'])},
|
|
237
|
+
reason)
|
|
185
238
|
advance_to_next_task()
|
|
186
239
|
return
|
|
187
240
|
|
|
188
241
|
if action == 'fail':
|
|
189
242
|
task['status'] = 'failed'
|
|
190
243
|
task['actions'].append({"action": "fail", "reason": reason})
|
|
244
|
+
_log_jinx(f"plonk-task-{ui.current_task}-fail", _npc_name,
|
|
245
|
+
{"task": task['text'], "type": "task_failed", "total_actions": len(task['actions'])},
|
|
246
|
+
reason, status="error", error_msg=reason)
|
|
191
247
|
advance_to_next_task()
|
|
192
248
|
return
|
|
193
249
|
|
|
@@ -196,20 +252,25 @@ steps:
|
|
|
196
252
|
|
|
197
253
|
if action == 'click':
|
|
198
254
|
x, y = action_response.get('x', 50), action_response.get('y', 50)
|
|
199
|
-
perform_action(
|
|
255
|
+
perform_action({"type": "click", "x": x, "y": y})
|
|
200
256
|
act_record['x'] = x
|
|
201
257
|
act_record['y'] = y
|
|
202
258
|
elif action == 'type':
|
|
203
259
|
txt = action_response.get('text', '')
|
|
204
|
-
perform_action(
|
|
260
|
+
perform_action({"type": "type", "text": txt})
|
|
205
261
|
act_record['text'] = txt
|
|
206
262
|
elif action == 'key':
|
|
207
263
|
key = action_response.get('text', 'enter')
|
|
208
|
-
|
|
264
|
+
# Detect combo keys like ctrl+l, ctrl+a, shift+tab
|
|
265
|
+
if '+' in key:
|
|
266
|
+
parts = [k.strip() for k in key.split('+')]
|
|
267
|
+
perform_action({"type": "hotkey", "keys": parts})
|
|
268
|
+
else:
|
|
269
|
+
perform_action({"type": "key", "keys": key})
|
|
209
270
|
act_record['key'] = key
|
|
210
271
|
elif action == 'launch':
|
|
211
272
|
cmd = action_response.get('command', '')
|
|
212
|
-
perform_action(
|
|
273
|
+
perform_action({"type": "shell", "command": cmd})
|
|
213
274
|
act_record['command'] = cmd
|
|
214
275
|
time.sleep(2)
|
|
215
276
|
elif action == 'wait':
|
|
@@ -219,7 +280,13 @@ steps:
|
|
|
219
280
|
|
|
220
281
|
task['actions'].append(act_record)
|
|
221
282
|
ui.status = action + " - " + reason[:40]
|
|
222
|
-
|
|
283
|
+
# Wait for UI to settle after state-changing actions
|
|
284
|
+
if action in ('key', 'click'):
|
|
285
|
+
time.sleep(2.0)
|
|
286
|
+
elif action == 'type':
|
|
287
|
+
time.sleep(0.5)
|
|
288
|
+
else:
|
|
289
|
+
time.sleep(0.3)
|
|
223
290
|
|
|
224
291
|
if ui.mode == 'step':
|
|
225
292
|
ui.mode = 'paused'
|
|
@@ -227,6 +294,10 @@ steps:
|
|
|
227
294
|
|
|
228
295
|
except Exception as e:
|
|
229
296
|
task['actions'].append({"action": "error", "reason": str(e)})
|
|
297
|
+
_log_jinx(f"plonk-task-{ui.current_task}-iter-{ui.iteration}-error",
|
|
298
|
+
npc.name if npc and hasattr(npc, 'name') else "plonk",
|
|
299
|
+
{"task": task['text'] if task else "unknown", "iteration": ui.iteration, "type": "error"},
|
|
300
|
+
str(e), status="error", error_msg=str(e))
|
|
230
301
|
ui.status = "Error: " + str(e)[:40]
|
|
231
302
|
|
|
232
303
|
def advance_to_next_task():
|
|
@@ -363,7 +434,7 @@ steps:
|
|
|
363
434
|
if a.get('x') is not None:
|
|
364
435
|
coords = "(" + str(a.get('x', '')) + "," + str(a.get('y', '')) + ") "
|
|
365
436
|
elif a.get('text'):
|
|
366
|
-
coords = '"' + str(a['text'])[:
|
|
437
|
+
coords = '"' + str(a['text'])[:40] + '" '
|
|
367
438
|
elif a.get('key'):
|
|
368
439
|
coords = '[' + str(a['key']) + '] '
|
|
369
440
|
elif a.get('command'):
|