@xingjisoft/neutrino 0.6.0 → 0.7.0

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.
Files changed (169) hide show
  1. package/README.md +6 -1
  2. package/assets/fast-planner.txt +57 -0
  3. package/assets/fast-replanner.txt +28 -0
  4. package/dist/agents.d.ts +25 -0
  5. package/dist/app.d.ts +206 -0
  6. package/dist/app.js +15 -0
  7. package/dist/background.d.ts +96 -0
  8. package/dist/bin.d.ts +2 -0
  9. package/dist/bin.js +40 -3
  10. package/dist/browser.d.ts +18 -0
  11. package/dist/clipboard.d.ts +16 -0
  12. package/dist/colors.d.ts +29 -0
  13. package/dist/compact.d.ts +52 -0
  14. package/dist/config.d.ts +221 -0
  15. package/dist/config.js +22 -7
  16. package/dist/cwd.d.ts +19 -0
  17. package/dist/fast/artifacts.d.ts +26 -0
  18. package/dist/fast/artifacts.js +109 -0
  19. package/dist/fast/engine.d.ts +78 -0
  20. package/dist/fast/engine.js +657 -0
  21. package/dist/fast/evidence.d.ts +21 -0
  22. package/dist/fast/evidence.js +68 -0
  23. package/dist/fast/planner.d.ts +10 -0
  24. package/dist/fast/planner.js +51 -0
  25. package/dist/fast/pool.d.ts +17 -0
  26. package/dist/fast/pool.js +78 -0
  27. package/dist/fast/scheduler.d.ts +54 -0
  28. package/dist/fast/scheduler.js +383 -0
  29. package/dist/fast/types.d.ts +100 -0
  30. package/dist/fast/types.js +21 -0
  31. package/dist/fast/worker.d.ts +23 -0
  32. package/dist/fast/worker.js +185 -0
  33. package/dist/header.d.ts +3 -0
  34. package/dist/index.d.ts +5 -0
  35. package/dist/llm/approval.d.ts +204 -0
  36. package/dist/llm/approval.js +14 -3
  37. package/dist/llm/client.d.ts +118 -0
  38. package/dist/llm/client.js +57 -8
  39. package/dist/llm/codeExecutor.d.ts +14 -0
  40. package/dist/llm/codemode.d.ts +15 -0
  41. package/dist/llm/context.d.ts +29 -0
  42. package/dist/llm/context.js +17 -4
  43. package/dist/llm/diagnostics.d.ts +91 -0
  44. package/dist/llm/diagnostics.js +242 -0
  45. package/dist/llm/plan.d.ts +148 -0
  46. package/dist/llm/retry.d.ts +80 -0
  47. package/dist/llm/risk.d.ts +21 -0
  48. package/dist/llm/risk.js +7 -0
  49. package/dist/llm/runtime.d.ts +189 -0
  50. package/dist/llm/runtime.js +178 -24
  51. package/dist/llm/search.d.ts +2 -0
  52. package/dist/llm/searchNetwork.d.ts +8 -0
  53. package/dist/llm/subagent.d.ts +112 -0
  54. package/dist/llm/subagent.js +25 -12
  55. package/dist/llm/tools.d.ts +20 -0
  56. package/dist/llm/tools.js +4 -1
  57. package/dist/llm/workspace.d.ts +6 -0
  58. package/dist/llm/workspace.js +271 -0
  59. package/dist/markdown.d.ts +58 -0
  60. package/dist/migration.d.ts +8 -0
  61. package/dist/persistence.d.ts +14 -0
  62. package/dist/plugins/catalog.d.ts +20 -0
  63. package/dist/plugins/catalog.js +45 -0
  64. package/dist/plugins/cli.d.ts +1 -0
  65. package/dist/plugins/cli.js +106 -0
  66. package/dist/plugins/host.d.ts +121 -0
  67. package/dist/plugins/host.js +450 -0
  68. package/dist/plugins/manager.d.ts +24 -0
  69. package/dist/plugins/manager.js +288 -0
  70. package/dist/plugins/package.d.ts +5 -0
  71. package/dist/plugins/package.js +53 -0
  72. package/dist/plugins/runner.d.ts +1 -0
  73. package/dist/plugins/runner.js +227 -0
  74. package/dist/plugins/session.d.ts +12 -0
  75. package/dist/plugins/session.js +97 -0
  76. package/dist/plugins/terminal.d.ts +22 -0
  77. package/dist/plugins/terminal.js +33 -0
  78. package/dist/plugins/types.d.ts +178 -0
  79. package/dist/plugins/types.js +6 -0
  80. package/dist/plugins/validation.d.ts +14 -0
  81. package/dist/plugins/validation.js +174 -0
  82. package/dist/plugins/web-sdk.d.ts +58 -0
  83. package/dist/plugins/web-sdk.js +1 -0
  84. package/dist/pro/agents.d.ts +34 -0
  85. package/dist/pro/agents.js +1 -1
  86. package/dist/pro/config.d.ts +75 -0
  87. package/dist/pro/config.js +3 -2
  88. package/dist/pro/preflight.d.ts +21 -0
  89. package/dist/sanitize.d.ts +34 -0
  90. package/dist/screens/agentConversation.d.ts +28 -0
  91. package/dist/screens/agentHistory.d.ts +25 -0
  92. package/dist/screens/agentSelect.d.ts +16 -0
  93. package/dist/screens/chat.d.ts +292 -0
  94. package/dist/screens/chat.js +179 -65
  95. package/dist/screens/history.d.ts +23 -0
  96. package/dist/screens/modelSelect.d.ts +29 -0
  97. package/dist/screens/plugins.d.ts +29 -0
  98. package/dist/screens/plugins.js +196 -0
  99. package/dist/screens/settings.d.ts +42 -0
  100. package/dist/screens/start.d.ts +43 -0
  101. package/dist/screens/start.js +25 -17
  102. package/dist/screens/updatePrompt.d.ts +41 -0
  103. package/dist/selection.d.ts +75 -0
  104. package/dist/state.d.ts +26 -0
  105. package/dist/terminal/buffer.d.ts +47 -0
  106. package/dist/terminal/input.d.ts +70 -0
  107. package/dist/terminal/renderer.d.ts +43 -0
  108. package/dist/terminal/restore.d.ts +41 -0
  109. package/dist/terminal/screen.d.ts +57 -0
  110. package/dist/tray.d.ts +14 -0
  111. package/dist/ui.d.ts +123 -0
  112. package/dist/update.d.ts +71 -0
  113. package/dist/updateWatch.d.ts +43 -0
  114. package/dist/web/approvalQueue.d.ts +8 -0
  115. package/dist/web/approvalQueue.js +59 -0
  116. package/dist/web/auth.d.ts +14 -0
  117. package/dist/web/clientState.d.ts +16 -0
  118. package/dist/web/folderPicker.d.ts +34 -0
  119. package/dist/web/folderPicker.js +544 -0
  120. package/dist/web/hub.d.ts +213 -0
  121. package/dist/web/hub.js +14 -2
  122. package/dist/web/localApi.d.ts +11 -0
  123. package/dist/web/localApi.js +260 -0
  124. package/dist/web/plugins.d.ts +8 -0
  125. package/dist/web/plugins.js +64 -0
  126. package/dist/web/server.d.ts +81 -0
  127. package/dist/web/server.js +127 -19
  128. package/dist/web/service.d.ts +7 -0
  129. package/dist/web/service.js +1 -0
  130. package/dist/web/session.d.ts +176 -0
  131. package/dist/web/session.js +170 -21
  132. package/dist/web/settings.d.ts +65 -0
  133. package/dist/web/terminalBridge.d.ts +15 -0
  134. package/dist/web/terminalBridge.js +8 -2
  135. package/dist/width.d.ts +6 -0
  136. package/docs/plugins/README.md +126 -0
  137. package/docs/plugins/agent-sdk.md +92 -0
  138. package/docs/plugins/architecture.md +64 -0
  139. package/docs/plugins/compatibility.md +34 -0
  140. package/docs/plugins/development.md +55 -0
  141. package/docs/plugins/manifest.md +59 -0
  142. package/docs/plugins/manifest.schema.json +286 -0
  143. package/docs/plugins/security.md +53 -0
  144. package/docs/plugins/skills-prompts.md +69 -0
  145. package/docs/plugins/ui-sdk.md +109 -0
  146. package/examples/local-api.py +105 -0
  147. package/examples/plugins/cards/card.html +6 -0
  148. package/examples/plugins/cards/neutrino.plugin.json +42 -0
  149. package/examples/plugins/composer/editor.html +10 -0
  150. package/examples/plugins/composer/neutrino.plugin.json +10 -0
  151. package/examples/plugins/function-tools/agent.js +32 -0
  152. package/examples/plugins/function-tools/neutrino.plugin.json +15 -0
  153. package/examples/plugins/menus-tabs/neutrino.plugin.json +15 -0
  154. package/examples/plugins/menus-tabs/panel.html +6 -0
  155. package/examples/plugins/skills-prompts/neutrino.plugin.json +13 -0
  156. package/examples/plugins/skills-prompts/prompts/evidence.md +1 -0
  157. package/examples/plugins/skills-prompts/prompts/reviewer.md +1 -0
  158. package/examples/plugins/skills-prompts/skills/review/SKILL.md +8 -0
  159. package/examples/plugins/theme/neutrino.plugin.json +12 -0
  160. package/package.json +66 -42
  161. package/web/LOCAL-API.md +65 -0
  162. package/web/README.md +30 -8
  163. package/web/app.js +256 -67
  164. package/web/i18n.js +18 -3
  165. package/web/index.html +3 -1
  166. package/web/plugins.css +10 -0
  167. package/web/plugins.js +230 -0
  168. package/web/style.css +12 -3
  169. package/web/turns.js +7 -8
@@ -0,0 +1,69 @@
1
+ # Skill 与提示词
2
+
3
+ ## Skill 格式和触发
4
+
5
+ ```text
6
+ skills/review/
7
+ SKILL.md
8
+ references/
9
+ scripts/
10
+ assets/
11
+ ```
12
+
13
+ `SKILL.md` 使用简短 front matter:
14
+
15
+ ```md
16
+ ---
17
+ name: review
18
+ description: Review code for concrete regressions and verification gaps.
19
+ ---
20
+
21
+ Read the changes and their callers. Report findings supported by evidence.
22
+ ```
23
+
24
+ 本版解析单行 name / description;复杂 YAML、折叠字符串不属于此格式。name 应使用便于命令引用的短名称,例如 `review`。
25
+
26
+ 清单注册:
27
+
28
+ ```json
29
+ { "permissions": ["prompts.contribute"],
30
+ "contributes": { "skills": ["skills/review/SKILL.md"] } }
31
+ ```
32
+
33
+ 代码插件也可在 activate 中使用 `ctx.skills.register('skills/review/SKILL.md')`。
34
+
35
+ 提示词首先只包含 skill 名称和描述。完整正文仅在当前请求使用以下显式引用时加入:
36
+
37
+ ```text
38
+ /skill community.review-skills/review 审查这次修改
39
+ ```
40
+
41
+ 浏览器输入框工具栏提供 skill 选择器。普通文本提到某个名称不会自动执行脚本或加载全部 skill 正文。
42
+
43
+ resources 目录随包分发。skill 中的脚本不会自动执行;需要可执行能力时,另注册工具或命令,由宿主能力桥接执行。纯 skill 不需要 JS 入口。现有文件夹可添加最小的清单后直接安装。
44
+
45
+ ## 提示词片段
46
+
47
+ ```json
48
+ {
49
+ "permissions": ["prompts.contribute"],
50
+ "contributes": {
51
+ "prompts": [
52
+ { "id": "style", "file": "prompts/style.md", "slot": "rules", "order": 100 },
53
+ { "id": "persona", "text": "You are a careful reviewer.", "slot": "persona", "replace": true, "roles": ["main"] }
54
+ ]
55
+ }
56
+ }
57
+ ```
58
+
59
+ 先按 persona / rules / context 排序,再按 order 和完整 ID 排序。`roles` 省略时用于适用的业务 Agent,指定时精确匹配运行角色。常见角色包括 main、subagent,以及 main_model、plan_model、backend_output_model 等 Pro 角色。审批角色 confirm_model / reviewer 不接受插件人格与片段注入。
60
+
61
+ replace 片段只有在 `persona` 提供者中被选中才生效,其他 replace 项不自动追加。普通片段加在所选基础提示词之后,并带有来源 ID。动态、与模型调用相关的上下文可以通过 request.before 的 systemSuffix 提供。
62
+
63
+ 宿主任务原始约束、权限、预算和执行策略由运行时另外保留。改写人格或规则不能授予文件权限、扩展子 Agent 的写入范围或跳过审批。
64
+
65
+ ## 检查实际生效内容
66
+
67
+ 插件页的“当前回合诊断”显示最近的插件提示词片段和运行错误。`task_run.plugins` 保存包版本与配置;任务 journal 的 `plugin_prompt` 和 `plugin_request_suffix` 保存组装与请求补充记录。
68
+
69
+ 配置或文件更新不会改变已经开始的回合。需要验证新片段时,保存配置后开始下一回合。子 Agent 使用同一份插件版本组合,并按自身角色选择提示词。
@@ -0,0 +1,109 @@
1
+ # Web / TUI 界面 SDK
2
+
3
+ ## 插槽
4
+
5
+ | 插槽 | 组合方式 | 用途 |
6
+ | --- | --- | --- |
7
+ | app.menu | 多项追加 | 插件菜单按钮 |
8
+ | workspace.tabs | 多项追加 | 工作区选项卡 / 面板 |
9
+ | sidebar.footer | 多项追加 | 侧栏信息 |
10
+ | chat.header.actions | 多项追加 | 对话标题操作 |
11
+ | chat.composer.toolbar | 多项追加 | 输入辅助、预设文本 |
12
+ | chat.composer | 显式选择一个提供者 | 输入编辑器 |
13
+ | chat.message.actions | 按消息匹配追加 | 消息操作按钮 |
14
+ | chat.message.renderer | 显式选择一个提供者 | 消息正文视图 |
15
+ | chat.card.plan | 显式选择一个提供者 | 计划卡片 |
16
+ | chat.card.tool | 显式选择一个提供者 | 工具结果卡片 |
17
+ | settings.sections | 声明设置页面 | 插件自己的设置相关内容 |
18
+
19
+ 追加项按 order 和完整 key 稳定排序。替换项必须在管理界面或 `plugin provider` 命令中选择;安装或排序靠前不会自动接管输入框。默认配置表单由管理器根据 configuration 自动生成。
20
+
21
+ ```json
22
+ {
23
+ "id": "panel",
24
+ "slot": "workspace.tabs",
25
+ "title": "My panel",
26
+ "view": "panel.html",
27
+ "height": 240,
28
+ "text": "Terminal fallback text",
29
+ "platforms": ["web", "tui"]
30
+ }
31
+ ```
32
+
33
+ `id` 在包内唯一,完整 key 为 `<插件ID>/<id>`。`view` 是包内 HTML 文件;`height` 为 40–1200 像素。`text` 提供无需脚本的内容及终端回退。`command` 是插件自己的用户命令名;点击时通过活动会话提交 `/plugin` 命令。`insert` 将文本加入当前草稿。`match.role` 和 `match.tool` 可限定消息 / 工具卡片。
34
+
35
+ ## HTML 视图
36
+
37
+ HTML 可以包含自己的样式与脚本。宿主在插件脚本之前注入 `window.neutrino`:
38
+
39
+ ```html
40
+ <button id="read">Read draft</button><pre id="output"></pre>
41
+ <script>
42
+ document.getElementById('read').onclick = async () => {
43
+ const draft = await neutrino.composer.read();
44
+ document.getElementById('output').textContent = draft.text;
45
+ };
46
+ </script>
47
+ ```
48
+
49
+ 这需要清单的 `composer.read` 权限。建议处理 Promise 错误;未处理的脚本错误会触发视图回退。
50
+
51
+ 每个 iframe 使用 `sandbox="allow-scripts"`,不启用 same-origin。CSP 禁止网络请求、表单提交、外部脚本和任意资源加载;可使用内联样式、内联脚本、打包的模块与 data 图片。视图资源通过已认证的 JSON API 读取后嵌入,不作为同源 HTML 执行。
52
+
53
+ 如使用单文件 ESM,可设置 `entries.web`:
54
+
55
+ ```js
56
+ export default {
57
+ mount(api, root) {
58
+ root.textContent = api.context.title;
59
+ },
60
+ };
61
+ ```
62
+
63
+ 可从 `@xingjisoft/neutrino/plugin/web` 导入类型和 `defineWebPlugin`,构建时将依赖打包为一个 ES 模块。运行时不解析 npm 裸模块名。
64
+
65
+ ## 浏览器 API
66
+
67
+ | 接口 | 权限 / 行为 |
68
+ | --- | --- |
69
+ | `neutrino.context` | 插件 ID、贡献 ID、标题、profile;读取会话权限存在时包含当前消息或计划 |
70
+ | `composer.read()` | composer.read,返回 `{ text, revision }` |
71
+ | `composer.write({ text, expectedRevision })` | composer.write,版本不匹配时拒绝,返回新版本 |
72
+ | `composer.submit()` | composer.submit,通过宿主正常发送流程提交当前草稿 |
73
+ | `conversation.read()` | conversation.read,返回当前只读会话投影 |
74
+ | `commands.execute(name, args)` | commands.register,只能调用本插件命令,经活动会话执行 |
75
+ | `configuration.read()` / `configuration.write({ values, expectedRevision })` | ui.contribute,仅当前插件配置;写入当前项目覆盖层,配置由服务端验证 |
76
+ | `view.resize(height)` | 将当前 iframe 高度限制到 40–1200 |
77
+ | `on('context.changed', handler)` | 订阅会话 / 草稿相关刷新,返回取消订阅函数 |
78
+
79
+ 宿主检查消息来源窗口、随机 nonce、当前会话身份、消息速率和每项权限。切换对话后旧 iframe 被释放。插件不能经此 API 直接批准请求或修改已保存消息。
80
+
81
+ 编辑草稿前先 read,写入时使用读取的 revision;发生冲突后重新 read,让用户决定如何合并。宿主 textarea 保留为草稿来源,iframe 成功准备后才隐藏它。停用、卸载、组件错误或十秒初始化超时会恢复默认组件和草稿。发送、停止、模式和审批控件仍由宿主维护。
82
+
83
+ 工具和消息原文保留在宿主的回退区域;成功加载的自定义视图只改变呈现。插件被移除后,文本仍能显示。
84
+
85
+ ## 主题
86
+
87
+ `contributes.theme` 设置简单的 CSS 变量值。例如:
88
+
89
+ ```json
90
+ {
91
+ "--bg": "#171725",
92
+ "--accent": "#b8b2ff",
93
+ "--neutrino-composer-radius": "12px",
94
+ "--neutrino-message-radius": "12px",
95
+ "--neutrino-tui-accent": "#b8b2ff"
96
+ }
97
+ ```
98
+
99
+ 支持已有 Web 设计变量;`--neutrino-composer-radius` 和 `--neutrino-message-radius` 专用于聊天外观。CSS 值不允许 URL、表达式、规则或分号。移除主题时恢复原先变量。
100
+
101
+ ## TUI
102
+
103
+ TUI 读取同一份声明。app.menu 和 workspace.tabs 出现在起始页;命令通过同一会话运行,文本页面支持返回和滚动。Web 专用组件使用 `platforms: ["web"]`,避免在终端显示无法使用的界面。
104
+
105
+ 选择了提供者后,终端 text 模板支持 `{{content}}`、`{{role}}`(消息),`{{name}}`、`{{result}}`、`{{arguments}}`(工具),`{{plan}}`(计划)和 `{{text}}`(输入提示)。模板替换显示文本,保留原始历史和输入行为。
106
+
107
+ 终端主题支持 `--neutrino-tui-accent`、`--neutrino-tui-tool-header`、`--neutrino-tui-user`、`--neutrino-tui-assistant`、`--neutrino-tui-dim`,值为六位十六进制颜色。取消主题会恢复内置颜色。审批状态色不受这些变量覆盖。
108
+
109
+ v1 不把终端 Buffer、原始 stdin/stdout 或内部 App 暴露给脚本。新的键鼠交互组件应通过宿主后续版本的公开契约扩展,避免依赖私有渲染实现。完整 HTML 编辑器和任意 JavaScript 卡片在 Web 中提供。
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env python3
2
+ """Start a local Neutrino conversation and handle its approval requests."""
3
+ import argparse
4
+ import json
5
+ import os
6
+ from pathlib import Path
7
+ import sys
8
+ from urllib.error import HTTPError
9
+ from urllib.request import Request, urlopen
10
+
11
+ parser = argparse.ArgumentParser()
12
+ parser.add_argument("message")
13
+ parser.add_argument("--cwd", default=os.getcwd())
14
+ parser.add_argument("--provider", required=True)
15
+ parser.add_argument("--model", required=True)
16
+ parser.add_argument("--intensity", default="High")
17
+ parser.add_argument("--mode", choices=["standard", "ptc", "fast", "fast_dag"], default="standard")
18
+ args = parser.parse_args()
19
+
20
+ home = Path(os.environ.get("NEUTRINO_HOME", Path.home() / ".neutrino"))
21
+ connection = json.loads((home / "api.json").read_text(encoding="utf-8"))
22
+ base = connection["url"]
23
+ headers = {"Authorization": "Bearer " + connection["token"]}
24
+
25
+
26
+ def request(route, data=None):
27
+ request_headers = dict(headers)
28
+ payload = None
29
+ if data is not None:
30
+ payload = json.dumps(data).encode()
31
+ request_headers["Content-Type"] = "application/json"
32
+ try:
33
+ with urlopen(Request(base + route, payload, request_headers), timeout=30) as response:
34
+ return json.load(response)
35
+ except HTTPError as error:
36
+ raise RuntimeError(error.read().decode()) from error
37
+
38
+
39
+ conversation = request("/conversations", {
40
+ "cwd": os.path.abspath(args.cwd),
41
+ "selection": {"provider": args.provider, "model": args.model, "intensity": args.intensity},
42
+ "agent_mode": args.mode,
43
+ "approval_mode": "request",
44
+ })
45
+ key = conversation["key"]
46
+ print(f"Conversation: {key}", file=sys.stderr)
47
+ # Subscribe before sending: events generated after this point are retained for
48
+ # reconnect, and the initial snapshot describes the newly-created conversation.
49
+ with urlopen(Request(f"{base}/live/{key}/events", headers=headers)) as stream:
50
+ request(f"/live/{key}/message", {"text": args.message})
51
+ previous_text = ""
52
+ printed_messages = set()
53
+ previous_status = None
54
+ handled = set()
55
+ event = "message"
56
+ data = []
57
+ for raw in stream:
58
+ line = raw.decode("utf-8").rstrip("\r\n")
59
+ if line.startswith("event:"):
60
+ event = line[6:].strip()
61
+ elif line.startswith("data:"):
62
+ data.append(line[5:].lstrip())
63
+ elif not line:
64
+ if not data:
65
+ continue
66
+ value = json.loads("\n".join(data))
67
+ data = []
68
+ if event == "snapshot":
69
+ for index, message in enumerate(value.get("messages", [])):
70
+ if message.get("role") != "assistant" or index in printed_messages:
71
+ continue
72
+ completed = message.get("content", "")
73
+ if previous_text and completed.startswith(previous_text):
74
+ print(completed[len(previous_text):], end="", flush=True)
75
+ else:
76
+ print("\n" + completed, end="", flush=True)
77
+ printed_messages.add(index)
78
+ previous_text = ""
79
+ text = value.get("streamingContent") or ""
80
+ if text.startswith(previous_text):
81
+ print(text[len(previous_text):], end="", flush=True)
82
+ elif text:
83
+ print("\n" + text, end="", flush=True)
84
+ previous_text = text
85
+ status = (value.get("busy"), value.get("paused"), bool(value.get("reviewing")))
86
+ if status != previous_status:
87
+ print(f"\n[busy={status[0]} paused={status[1]} reviewing={status[2]}]", file=sys.stderr)
88
+ previous_status = status
89
+ approval = value.get("approval")
90
+ elif event == "approval.requested":
91
+ approval = value
92
+ elif event == "gone":
93
+ break
94
+ else:
95
+ approval = None
96
+ if approval and approval.get("id") not in handled:
97
+ approval_id = approval["id"]
98
+ handled.add(approval_id)
99
+ print("\nApproval requested:\n" + json.dumps(approval, indent=2, ensure_ascii=False), file=sys.stderr)
100
+ answer = input("Approve? [y/N] ").strip().lower() == "y"
101
+ try:
102
+ request(f"/live/{key}/approvals/{approval_id}", {"approved": answer})
103
+ except RuntimeError as error:
104
+ print(f"Approval could not be applied: {error}", file=sys.stderr)
105
+ event = "message"
@@ -0,0 +1,6 @@
1
+ <!doctype html><html><head><meta charset="utf-8"><style>
2
+ body{box-sizing:border-box;margin:0;padding:14px;background:#1c2635;color:#e3ecf8;border:1px solid #54749d;border-radius:10px;font:13px system-ui}h3{margin:0 0 8px;color:#a6ccff}pre{white-space:pre-wrap;overflow-wrap:anywhere;max-height:250px;overflow:auto}
3
+ </style></head><body><h3 id="title"></h3><pre id="result"></pre><script>
4
+ function render(){const c=neutrino.context;document.getElementById('title').textContent=c.title;document.getElementById('result').textContent=JSON.stringify(c.message||c.plan||{},null,2)}
5
+ neutrino.on('context.changed',render);render();
6
+ </script></body></html>
@@ -0,0 +1,42 @@
1
+ {
2
+ "id": "community.inspector-cards",
3
+ "name": "Inspector Cards / 检查卡片",
4
+ "version": "1.0.0",
5
+ "apiVersion": 1,
6
+ "categories": [
7
+ "appearance"
8
+ ],
9
+ "description": "Alternative tool, plan and message renderers. Select each provider in Plugins.",
10
+ "permissions": [
11
+ "ui.contribute",
12
+ "conversation.read"
13
+ ],
14
+ "contributes": {
15
+ "ui": [
16
+ {
17
+ "id": "tool",
18
+ "slot": "chat.card.tool",
19
+ "title": "Inspector tool card / 工具卡片",
20
+ "view": "card.html",
21
+ "height": 200,
22
+ "text": "{{name}}\n{{result}}"
23
+ },
24
+ {
25
+ "id": "plan",
26
+ "slot": "chat.card.plan",
27
+ "title": "Inspector plan / 计划卡片",
28
+ "view": "card.html",
29
+ "height": 220,
30
+ "text": "Plan\n{{plan}}"
31
+ },
32
+ {
33
+ "id": "message",
34
+ "slot": "chat.message.renderer",
35
+ "title": "Inspector message / 消息卡片",
36
+ "view": "card.html",
37
+ "height": 160,
38
+ "text": "{{content}}"
39
+ }
40
+ ]
41
+ }
42
+ }
@@ -0,0 +1,10 @@
1
+ <!doctype html><html><head><meta charset="utf-8"><style>
2
+ body{margin:0;padding:8px;background:#202127;color:#eee;font:13px system-ui}textarea{box-sizing:border-box;width:100%;height:115px;background:#17181d;color:inherit;border:1px solid #586a91;border-radius:8px;padding:10px;resize:none}footer{display:flex;justify-content:space-between;align-items:center}button{background:#b8cbff;border:0;border-radius:6px;padding:6px 15px}#status{color:#c3cada}
3
+ </style></head><body><textarea id="draft" aria-label="Writing draft / 写作草稿" placeholder="Write here / 在这里写作…"></textarea><footer><span id="status">Writing editor / 写作编辑器</span><button id="send">Send / 发送</button></footer><script>
4
+ const input=document.getElementById('draft'),status=document.getElementById('status');let revision=0,writing=Promise.resolve();
5
+ async function sync(){const draft=await neutrino.composer.read();revision=draft.revision;if(document.activeElement!==input)input.value=draft.text}
6
+ input.oninput=()=>{const text=input.value;writing=writing.then(async()=>{const result=await neutrino.composer.write({text,expectedRevision:revision});revision=result.revision;status.textContent=Array.from(text).length+' characters / 字'}).catch(async e=>{status.textContent=e.message;await sync()})};
7
+ document.getElementById('send').onclick=async()=>{await writing;try{await neutrino.composer.submit();input.value='';await sync()}catch(e){status.textContent=e.message}};
8
+ input.onkeydown=e=>{if((e.ctrlKey||e.metaKey)&&e.key==='Enter'){e.preventDefault();document.getElementById('send').click()}};
9
+ neutrino.on('context.changed',()=>sync().catch(e=>status.textContent=e.message));sync().catch(e=>status.textContent=e.message);
10
+ </script></body></html>
@@ -0,0 +1,10 @@
1
+ {
2
+ "id": "community.writing-composer", "name": "Writing Composer / 写作输入框",
3
+ "version": "1.0.0", "apiVersion": 1, "categories": ["function", "appearance"],
4
+ "description": "Replace the web editor while the host retains the draft, send and stop controls.",
5
+ "permissions": ["ui.contribute", "composer.read", "composer.write", "composer.submit"],
6
+ "contributes": { "ui": [
7
+ { "id": "editor", "slot": "chat.composer", "title": "Writing editor / 写作编辑器", "view": "editor.html", "height": 185, "platforms": ["web"] },
8
+ { "id": "outline", "slot": "chat.composer.toolbar", "title": "Outline / 提纲", "insert": "请先列出提纲,再完成任务:\n" }
9
+ ] }
10
+ }
@@ -0,0 +1,32 @@
1
+ // This module runs in QuickJS. Bundle third-party dependencies; Node globals are unavailable.
2
+ export default {
3
+ activate(ctx) {
4
+ const statistics = (text) => ({
5
+ label: ctx.config.label,
6
+ characters: Array.from(text).length,
7
+ words: text.trim() ? text.trim().split(/\s+/u).length : 0,
8
+ lines: text ? text.split(/\r?\n/u).length : 0,
9
+ });
10
+ ctx.tools.register({
11
+ name: "word_count", description: "Count characters, whitespace-separated words and lines in text.",
12
+ parameters: { type: "object", properties: { text: { type: "string", maxLength: 100000 } }, required: ["text"], additionalProperties: false },
13
+ effects: [], execute: ({ text }) => statistics(text),
14
+ });
15
+ ctx.tools.register({
16
+ name: "file_stats", description: "Read a text file through the shared approval gate and count its contents.",
17
+ parameters: { type: "object", properties: { path: { type: "string" }, reason: { type: "string" } }, required: ["path", "reason"], additionalProperties: false },
18
+ effects: ["filesystem.read"],
19
+ async execute({ path, reason }) { return statistics(await ctx.fs.read(path, reason)); },
20
+ });
21
+ ctx.commands.register({
22
+ name: "count", description: "Count text without a model request.",
23
+ parameters: { type: "object", properties: { text: { type: "string" } }, required: ["text"], additionalProperties: false },
24
+ effects: [], execute: ({ text }) => statistics(text),
25
+ });
26
+ ctx.events.on("turn.finished", async () => {
27
+ const count = await ctx.storage.get("turn-count");
28
+ await ctx.storage.set("turn-count", Number(count || 0) + 1);
29
+ });
30
+ ctx.effect(() => ctx.log("Text tools disposed"));
31
+ },
32
+ };
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "community.text-tools",
3
+ "name": "Text Tools / 文本工具",
4
+ "version": "1.0.0",
5
+ "apiVersion": 1,
6
+ "description": "Pure word counting, a read-only file summary, and a user command.",
7
+ "categories": ["function"],
8
+ "runtime": "sandbox",
9
+ "entries": { "agent": "agent.js" },
10
+ "permissions": ["tools.register", "commands.register", "filesystem.read", "hooks.register", "storage"],
11
+ "configuration": {
12
+ "type": "object", "additionalProperties": false,
13
+ "properties": { "label": { "type": "string", "default": "Text statistics", "maxLength": 80 } }
14
+ }
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "community.workbench",
3
+ "name": "Workbench / 工作台",
4
+ "version": "1.0.0", "apiVersion": 1,
5
+ "description": "Menus, a workspace tab and a sidebar panel, with terminal text fallbacks.",
6
+ "categories": ["function"],
7
+ "permissions": ["ui.contribute", "conversation.read"],
8
+ "contributes": {
9
+ "ui": [
10
+ { "id": "guide", "slot": "app.menu", "title": "Plugin guide / 插件指南", "text": "Plugins contribute menus, tabs, tools, skills and prompts through the public API. Use Plugins to install, enable and configure them." },
11
+ { "id": "workspace", "slot": "workspace.tabs", "title": "Workbench / 工作台", "view": "panel.html", "text": "Workbench: use /plugin commands and /skill references in a conversation.", "height": 240 },
12
+ { "id": "sidebar", "slot": "sidebar.footer", "title": "Extensions / 扩展", "text": "Your workspace can be extended without editing Neutrino." }
13
+ ]
14
+ }
15
+ }
@@ -0,0 +1,6 @@
1
+ <!doctype html><html><head><meta charset="utf-8"><style>
2
+ body{font:14px system-ui;color:#e6e8ee;background:#202127;padding:18px}h2{margin-top:0}pre{white-space:pre-wrap;color:#a9c9fa}
3
+ </style></head><body><h2>Workspace overview / 工作区概览</h2><pre id="info">Loading…</pre><script>
4
+ async function update(){try{const c=await neutrino.conversation.read();document.getElementById('info').textContent=JSON.stringify({cwd:c.cwd,messages:c.messages?.length||0,model:c.model},null,2)}catch(e){document.getElementById('info').textContent=e.message}}
5
+ neutrino.on('context.changed',update);update();
6
+ </script></body></html>
@@ -0,0 +1,13 @@
1
+ {
2
+ "id": "community.review-skills", "name": "Review Skills / 审查技能",
3
+ "version": "1.0.0", "apiVersion": 1, "categories": ["skill", "prompt"],
4
+ "description": "Lazy code-review skill, an additive rule and an optional replacement persona.",
5
+ "permissions": ["prompts.contribute"],
6
+ "contributes": {
7
+ "skills": ["skills/review/SKILL.md"],
8
+ "prompts": [
9
+ { "id": "evidence", "file": "prompts/evidence.md", "slot": "rules", "roles": ["main", "subagent", "main_model", "backend_output_model"] },
10
+ { "id": "reviewer", "file": "prompts/reviewer.md", "slot": "persona", "replace": true, "roles": ["main", "main_model"] }
11
+ ]
12
+ }
13
+ }
@@ -0,0 +1 @@
1
+ Support completion claims with actual checks. Explain compatibility implications when changing public interfaces. Preserve existing user changes.
@@ -0,0 +1 @@
1
+ You are Neutrino, a careful code reviewer. Investigate the relevant code and report actionable findings with clear evidence. Follow the user's requested scope and the host's execution policies.
@@ -0,0 +1,8 @@
1
+ ---
2
+ name: review
3
+ description: Review code changes for correctness, compatibility and missing validation.
4
+ ---
5
+
6
+ Read the changed files and their callers. Identify concrete regressions that can occur through a real execution path. Give each finding a file location, trigger, impact and suggested correction. Distinguish verified defects from questions. Run relevant existing checks when available. Finish with remaining verification gaps.
7
+
8
+ This skill supplies task guidance. File access, tools, approvals and task limits continue to be enforced by the host.
@@ -0,0 +1,12 @@
1
+ {
2
+ "id": "community.indigo-theme", "name": "Indigo / 靛蓝主题",
3
+ "version": "1.0.0", "apiVersion": 1, "categories": ["appearance"],
4
+ "description": "A declaration-only theme for both the browser and terminal.",
5
+ "permissions": ["ui.contribute"],
6
+ "contributes": { "theme": {
7
+ "--bg": "#171725", "--bg-sidebar": "#1c1c30", "--surface-1": "#24243b",
8
+ "--accent": "#b8b2ff", "--accent-strong": "#958bff", "--radius-lg": "12px",
9
+ "--neutrino-composer-radius": "12px", "--neutrino-message-radius": "12px",
10
+ "--neutrino-tui-accent": "#b8b2ff", "--neutrino-tui-tool-header": "#b8b2ff"
11
+ } }
12
+ }
package/package.json CHANGED
@@ -1,42 +1,66 @@
1
- {
2
- "name": "@xingjisoft/neutrino",
3
- "version": "0.6.0",
4
- "description": "XINGJI Neutrino - a CLI coding agent in the terminal.",
5
- "type": "module",
6
- "license": "Apache-2.0",
7
- "bin": {
8
- "neutrino": "dist/bin.js"
9
- },
10
- "main": "dist/app.js",
11
- "files": [
12
- "dist",
13
- "assets",
14
- "web"
15
- ],
16
- "engines": {
17
- "node": ">=18.17.0"
18
- },
19
- "scripts": {
20
- "build": "tsc -p tsconfig.json",
21
- "test": "npm run build && node scripts/test-paste.mjs && node scripts/test-mouse.mjs && node scripts/test-cursor.mjs && node scripts/test-selection.mjs && node scripts/test-drag-scroll.mjs && node scripts/test-selection-performance.mjs && node scripts/test-copy.mjs && node scripts/test-scrollbar.mjs && node scripts/test-render.mjs && node scripts/test-drift.mjs && node scripts/test-sanitize.mjs && node scripts/test-markdown.mjs && node scripts/test-wrap.mjs && node scripts/test-fetch.mjs && node scripts/test-web-search.mjs && node scripts/test-search-network.mjs && node scripts/test-compact.mjs && node scripts/test-history-fold.mjs && node scripts/test-web-history.mjs && node scripts/test-web-mobile-history.mjs && node scripts/test-session-migration.mjs && node scripts/test-web-session-migration.mjs && node scripts/test-web-default.mjs && node scripts/test-turn-fold.mjs && node scripts/test-long-turn.mjs && node scripts/test-plan.mjs && node scripts/test-agent-modes.mjs && node scripts/test-subagent.mjs && node scripts/test-task-runtime.mjs && node scripts/test-web-settings.mjs && node scripts/test-update.mjs && node scripts/test-update-ui.mjs && node scripts/test-update-web.mjs && node scripts/test-settings-update.mjs && node scripts/test-update-check.mjs && node scripts/test-update-watch.mjs && node scripts/test-model-intensity.mjs && node scripts/test-background.mjs && node scripts/test-background-lifetime.mjs && node scripts/test-web-markdown.mjs && node scripts/test-web-i18n.mjs && node scripts/test-web-workspaces.mjs && node scripts/test-tools.mjs && node scripts/test-approval.mjs && node scripts/test-approval-review.mjs && node scripts/test-approval-ui.mjs && node scripts/test-web-approval-details.mjs && node scripts/test-web-sidebar-status.mjs && node scripts/test-web-seen.mjs && node scripts/test-approval-mode.mjs && node scripts/test-approval-context.mjs && node scripts/test-settings-render.mjs && node scripts/test-approval-flow.mjs && node scripts/test-web.mjs && node scripts/test-web-settings-http.mjs && node scripts/test-web-lan.mjs && node scripts/test-web-auth.mjs && node scripts/test-web-auth-ui.mjs && node scripts/test-web-pro.mjs && node scripts/test-pro-preflight.mjs && node scripts/test-pro-runtime.mjs && node scripts/test-pro-team.mjs && node scripts/test-pro-ui.mjs && node scripts/test-interrupt.mjs && node scripts/test-terminal-lifecycle.mjs && node scripts/test-persistence.mjs && node scripts/test-resilience.mjs && node scripts/test-approval-timeout.mjs && node scripts/test-approval-hardening.mjs && node scripts/test-approval-extreme.mjs && node scripts/test-approval-reason.mjs",
22
- "prepare": "npm run build",
23
- "web": "node dist/bin.js web"
24
- },
25
- "keywords": [
26
- "cli",
27
- "agent",
28
- "ai",
29
- "terminal",
30
- "llm",
31
- "coding"
32
- ],
33
- "devDependencies": {
34
- "@types/node": "^22.0.0"
35
- },
36
- "dependencies": {
37
- "@cloudflare/codemode": "0.5.6",
38
- "quickjs-emscripten": "0.32.0",
39
- "typescript": "^5.5.0",
40
- "undici": "^6.29.1"
41
- }
42
- }
1
+ {
2
+ "name": "@xingjisoft/neutrino",
3
+ "version": "0.7.0",
4
+ "description": "XINGJI Neutrino - a CLI coding agent in the terminal.",
5
+ "type": "module",
6
+ "license": "Apache-2.0",
7
+ "bin": {
8
+ "neutrino": "dist/bin.js"
9
+ },
10
+ "main": "dist/app.js",
11
+ "files": [
12
+ "dist",
13
+ "assets",
14
+ "web",
15
+ "examples/plugins",
16
+ "examples/local-api.py",
17
+ "docs/plugins"
18
+ ],
19
+ "engines": {
20
+ "node": ">=18.17.0"
21
+ },
22
+ "scripts": {
23
+ "build": "tsc -p tsconfig.json",
24
+ "test": "npm run build && node scripts/test-paste.mjs && node scripts/test-mouse.mjs && node scripts/test-cursor.mjs && node scripts/test-selection.mjs && node scripts/test-drag-scroll.mjs && node scripts/test-selection-performance.mjs && node scripts/test-copy.mjs && node scripts/test-scrollbar.mjs && node scripts/test-render.mjs && node scripts/test-drift.mjs && node scripts/test-sanitize.mjs && node scripts/test-markdown.mjs && node scripts/test-wrap.mjs && node scripts/test-fetch.mjs && node scripts/test-web-search.mjs && node scripts/test-search-network.mjs && node scripts/test-compact.mjs && node scripts/test-history-fold.mjs && node scripts/test-web-history.mjs && node scripts/test-web-mobile-history.mjs && node scripts/test-session-migration.mjs && node scripts/test-web-session-migration.mjs && node scripts/test-web-default.mjs && node scripts/test-turn-fold.mjs && node scripts/test-long-turn.mjs && node scripts/test-plan.mjs && node scripts/test-agent-modes.mjs && node scripts/test-subagent.mjs && node scripts/test-task-runtime.mjs && node scripts/test-task-budget.mjs && node scripts/test-web-settings.mjs && node scripts/test-update.mjs && node scripts/test-update-ui.mjs && node scripts/test-update-web.mjs && node scripts/test-settings-update.mjs && node scripts/test-update-check.mjs && node scripts/test-update-watch.mjs && node scripts/test-model-intensity.mjs && node scripts/test-background.mjs && node scripts/test-background-lifetime.mjs && node scripts/test-web-markdown.mjs && node scripts/test-web-i18n.mjs && node scripts/test-web-workspaces.mjs && node scripts/test-web-folder-picker.mjs && node scripts/test-tools.mjs && node scripts/test-approval.mjs && node scripts/test-approval-review.mjs && node scripts/test-approval-ui.mjs && node scripts/test-web-approval-details.mjs && node scripts/test-web-sidebar-status.mjs && node scripts/test-web-seen.mjs && node scripts/test-approval-mode.mjs && node scripts/test-approval-context.mjs && node scripts/test-settings-render.mjs && node scripts/test-approval-flow.mjs && node scripts/test-web.mjs && node scripts/test-local-api.mjs && node scripts/test-web-settings-http.mjs && node scripts/test-web-lan.mjs && node scripts/test-web-auth.mjs && node scripts/test-web-auth-ui.mjs && node scripts/test-web-pro.mjs && node scripts/test-pro-preflight.mjs && node scripts/test-pro-runtime.mjs && node scripts/test-pro-team.mjs && node scripts/test-pro-ui.mjs && node scripts/test-interrupt.mjs && node scripts/test-terminal-lifecycle.mjs && node scripts/test-persistence.mjs && node scripts/test-resilience.mjs && node scripts/test-approval-timeout.mjs && node scripts/test-approval-hardening.mjs && node scripts/test-approval-extreme.mjs && node scripts/test-approval-reason.mjs && node scripts/test-plugins.mjs && node scripts/test-plugin-web.mjs && node scripts/test-fast-scheduler.mjs && node scripts/test-fast-engine.mjs && node scripts/test-fast-planner.mjs && node scripts/test-fast-verification.mjs && node scripts/test-fast-web.mjs && node scripts/test-fast-diagnostics.mjs && node scripts/test-fast-benchmark-budget.mjs && node scripts/test-fast-stability.mjs && node scripts/test-fast-efficiency.mjs",
25
+ "prepare": "npm run build",
26
+ "web": "node dist/bin.js web",
27
+ "test:plugins": "npm run build && node scripts/test-plugins.mjs && node scripts/test-plugin-web.mjs",
28
+ "test:fast": "npm run build && node scripts/test-fast-scheduler.mjs && node scripts/test-fast-engine.mjs && node scripts/test-fast-planner.mjs && node scripts/test-fast-verification.mjs && node scripts/test-fast-web.mjs && node scripts/test-fast-diagnostics.mjs && node scripts/test-fast-benchmark-budget.mjs && node scripts/test-fast-stability.mjs && node scripts/test-fast-efficiency.mjs",
29
+ "benchmark:fast": "npm run build && node scripts/benchmark-fast-scheduler.mjs",
30
+ "benchmark:fast:live": "npm run build && node scripts/benchmark-fast-live.mjs",
31
+ "test:fast:diagnostics": "npm run build && node scripts/test-fast-diagnostics.mjs && node scripts/test-fast-benchmark-budget.mjs && node scripts/test-fast-stability.mjs && node scripts/test-fast-efficiency.mjs",
32
+ "diagnose:fast:timeline": "npm run build && node scripts/diagnose-fast-timeline.mjs",
33
+ "report:fast:timeline": "node scripts/reanalyze-fast-timeline.mjs && node scripts/report-fast-timeline.mjs"
34
+ },
35
+ "keywords": [
36
+ "cli",
37
+ "agent",
38
+ "ai",
39
+ "terminal",
40
+ "llm",
41
+ "coding"
42
+ ],
43
+ "devDependencies": {
44
+ "@types/node": "^22.0.0",
45
+ "linkedom": "0.18.12"
46
+ },
47
+ "dependencies": {
48
+ "@cloudflare/codemode": "0.5.6",
49
+ "quickjs-emscripten": "0.32.0",
50
+ "typescript": "^5.5.0",
51
+ "undici": "^6.29.1"
52
+ },
53
+ "exports": {
54
+ ".": "./dist/app.js",
55
+ "./plugin": {
56
+ "types": "./dist/plugins/types.d.ts",
57
+ "import": "./dist/plugins/types.js"
58
+ },
59
+ "./plugin/web": {
60
+ "types": "./dist/plugins/web-sdk.d.ts",
61
+ "import": "./dist/plugins/web-sdk.js"
62
+ },
63
+ "./package.json": "./package.json",
64
+ "./dist/*": "./dist/*"
65
+ }
66
+ }