helloagents 3.0.35 → 3.0.37
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/.claude-plugin/marketplace.json +1 -4
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +11 -6
- package/README_CN.md +11 -6
- package/gemini-extension.json +1 -1
- package/install.ps1 +6 -1
- package/install.sh +8 -1
- package/package.json +1 -1
- package/scripts/cli-doctor-codex.mjs +4 -1
- package/scripts/cli-doctor.mjs +3 -3
- package/scripts/cli-hosts.mjs +1 -1
- package/scripts/cli-lifecycle-hosts.mjs +48 -20
- package/scripts/cli-messages.mjs +2 -2
- package/scripts/cli-runtime-root.mjs +9 -1
- package/scripts/notify-sound.mjs +2 -1
- package/scripts/project-session-cleanup.mjs +46 -0
- package/scripts/runtime-artifacts.mjs +2 -2
- package/scripts/runtime-scope.mjs +13 -10
- package/scripts/runtime-ttl.mjs +7 -4
- package/scripts/session-capsule.mjs +25 -1
- package/scripts/session-token.mjs +44 -9
- /package/hooks/{hooks.json → hooks-gemini.json} +0 -0
|
@@ -9,10 +9,7 @@
|
|
|
9
9
|
{
|
|
10
10
|
"name": "helloagents",
|
|
11
11
|
"description": "Quality-driven orchestration kernel for AI CLIs: intelligent routing, quality verification, safety guards, and notifications",
|
|
12
|
-
"source":
|
|
13
|
-
"source": "github",
|
|
14
|
-
"repo": "hellowind777/helloagents"
|
|
15
|
-
},
|
|
12
|
+
"source": "./",
|
|
16
13
|
"author": {
|
|
17
14
|
"name": "HelloWind",
|
|
18
15
|
"email": "hellowind777@gmail.com"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "helloagents",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.37",
|
|
4
4
|
"description": "HelloAGENTS — The orchestration kernel that makes any AI CLI smarter. Adds intelligent routing, unified QA gates, safety guards, and notifications.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "HelloWind",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "helloagents",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.37",
|
|
4
4
|
"description": "HelloAGENTS — Quality-driven orchestration kernel for AI CLIs with intelligent routing, unified QA gates, safety guards, and notifications.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "HelloWind",
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
**A workflow layer for AI coding CLIs: skills, project knowledge, delivery checks, safer config writes, and resumable execution.**
|
|
10
10
|
|
|
11
|
-
[](./package.json)
|
|
12
12
|
[](https://www.npmjs.com/package/helloagents)
|
|
13
13
|
[](./package.json)
|
|
14
14
|
[](./skills)
|
|
@@ -189,6 +189,8 @@ HelloAGENTS now resolves the current state file from `state_path`:
|
|
|
189
189
|
|
|
190
190
|
`<workspace>` is the current Git branch, `detached-<sha>` for a detached HEAD, or `workspace` for non-Git projects. `.helloagents/sessions/active.json` only records the active session index.
|
|
191
191
|
|
|
192
|
+
For project-local sessions, HelloAGENTS now only uses stable host identifiers such as `sessionId`, `conversationId`, `threadId`, or `HELLOAGENTS_NOTIFY_SESSION_ID`. It no longer uses terminal/window ids such as `WT_SESSION`, `TERM_SESSION_ID`, or `WINDOWID` to create extra project session directories.
|
|
193
|
+
|
|
192
194
|
`STATE.md` records where the current workflow stopped. It is not a universal memory file for every conversation. Codex `/goal` does not replace `state_path`, `turn-state`, or local evidence files; it only handles long-running continuation on the Codex side.
|
|
193
195
|
|
|
194
196
|
### 6) Verification and delivery evidence
|
|
@@ -206,6 +208,9 @@ Runtime state now stays intentionally small:
|
|
|
206
208
|
- `~/.codex/.helloagents/notify-state.json` for Codex-native closeout de-duplication only
|
|
207
209
|
|
|
208
210
|
`turn-state`, route context, and the artifact index are stored inside `STATE.md` metadata instead of a separate `capsule.json`. `events.jsonl` is opt-in trace output, not a default runtime file.
|
|
211
|
+
Project-local `STATE.md` is now materialized more lazily, and legacy root-level `.helloagents/artifacts/*.log` files are cleaned up automatically instead of growing as a second history system.
|
|
212
|
+
|
|
213
|
+
Standard runtime evidence and transient runtime state now expire after 72 hours. Long-running Codex goal flows still keep their 720-hour upper bound where the workflow explicitly needs it.
|
|
209
214
|
|
|
210
215
|
Delivery gate, guard, and QA gate messages use action-oriented wording such as processing path, closeout action, and visual validation action, so blocked flows show what to do next without turning executable steps into optional suggestions. Final closeout also enforces a single HelloAGENTS wrapper, so one reply does not emit duplicate closeout headers.
|
|
211
216
|
That wrapper is now reserved for direct final-user delivery only. Intermediate reports, delegated task results, and sub-agent replies stay natural, and sub-agent stop hooks reject wrapped closeout replies.
|
|
@@ -304,7 +309,7 @@ If you omit `--standby` or `--global`, HelloAGENTS first reuses the tracked/dete
|
|
|
304
309
|
|
|
305
310
|
### npm and one-shot script entries
|
|
306
311
|
|
|
307
|
-
Use these when you do not want to depend on the `helloagents` binary being available during package updates. In `HELLOAGENTS=target[:mode]`, target can be `all`, `claude`, `gemini`, or `codex`; mode can be `standby` or `global`. For install, an omitted mode is treated as `standby`. For update, cleanup, uninstall, and branch switching, an omitted mode is forwarded unchanged so HelloAGENTS can reuse the tracked or detected mode for that CLI first. For a custom tarball or package spec, set `HELLOAGENTS_PACKAGE` instead of `HELLOAGENTS_BRANCH`. For a guaranteed refresh of an already installed package, prefer `npm explore -g helloagents -- npm run sync-hosts -- ...` after the package command.
|
|
312
|
+
Use these when you do not want to depend on the `helloagents` binary being available during package updates. In `HELLOAGENTS=target[:mode]`, target can be `all`, `claude`, `gemini`, or `codex`; mode can be `standby` or `global`. For install, an omitted mode is treated as `standby`. For update, cleanup, uninstall, and branch switching, an omitted mode is forwarded unchanged so HelloAGENTS can reuse the tracked or detected mode for that CLI first. If you do not provide `HELLOAGENTS`, the one-shot install scripts now behave like plain package install: they install or update the package only and do not auto-deploy any host CLI. For a custom tarball or package spec, set `HELLOAGENTS_PACKAGE` instead of `HELLOAGENTS_BRANCH`. For a guaranteed refresh of an already installed package, prefer `npm explore -g helloagents -- npm run sync-hosts -- ...` after the package command.
|
|
308
313
|
|
|
309
314
|
Host configs use the stable `helloagents-js` entrypoint and runtime root `~/.helloagents/helloagents`, so Node global package paths can change without breaking managed hooks or Codex `notify`. Codex hooks use standalone `~/.codex/hooks.json` instead of adding large hook blocks to `config.toml`, and Codex global plugin roots plus plugin cache now link back to that same stable runtime root.
|
|
310
315
|
|
|
@@ -405,7 +410,7 @@ $env:HELLOAGENTS="codex:standby"; $env:HELLOAGENTS_ACTION="cleanup"; irm https:/
|
|
|
405
410
|
$env:HELLOAGENTS="gemini"; $env:HELLOAGENTS_ACTION="uninstall"; irm https://raw.githubusercontent.com/hellowind777/helloagents/main/install.ps1 | iex
|
|
406
411
|
```
|
|
407
412
|
|
|
408
|
-
The shell and PowerShell wrappers now parse `HELLOAGENTS` once, clear lifecycle env before update, branch switching, and uninstall, and then run one explicit sync or cleanup path.
|
|
413
|
+
The shell and PowerShell wrappers now parse `HELLOAGENTS` once, keep plain package install/update behavior when no target is specified, clear lifecycle env before update, branch switching, and uninstall, and then run one explicit sync or cleanup path.
|
|
409
414
|
|
|
410
415
|
### Branch switching
|
|
411
416
|
|
|
@@ -442,12 +447,12 @@ npm uninstall -g helloagents
|
|
|
442
447
|
| Gemini CLI | native extension install | managed by Gemini extension system |
|
|
443
448
|
| Codex CLI | native local-plugin chain | `~/.agents/plugins/marketplace.json`, `~/plugins/helloagents/ -> ~/.helloagents/helloagents`, `~/.codex/plugins/cache/local-plugins/helloagents/local/ -> ~/.helloagents/helloagents`, `~/.codex/config.toml`, `~/.codex/hooks.json`, `~/.codex/helloagents -> ~/.helloagents/helloagents` |
|
|
444
449
|
|
|
445
|
-
In global mode, HelloAGENTS now attempts the host-native install commands automatically. If a host command is unavailable, run the same commands manually:
|
|
450
|
+
In global mode, HelloAGENTS now attempts the host-native install commands automatically. For Claude Code, the marketplace should be added from the Git URL so the plugin source stays on HTTPS and avoids an SSH-only clone during installation. If a host command is unavailable, run the same commands manually:
|
|
446
451
|
|
|
447
452
|
```text
|
|
448
|
-
/plugin marketplace add hellowind777/helloagents
|
|
453
|
+
/plugin marketplace add https://github.com/hellowind777/helloagents.git
|
|
449
454
|
/plugin install helloagents@helloagents
|
|
450
|
-
|
|
455
|
+
helloagents install gemini --global
|
|
451
456
|
```
|
|
452
457
|
|
|
453
458
|
For Claude Code, the CLI also tries the equivalent `claude plugin marketplace add ...` and `claude plugin install ...` commands. The marketplace is named `helloagents`, and the plugin is also named `helloagents`, so the install target is `helloagents@helloagents`. Restart the host CLI after a global install.
|
package/README_CN.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
**面向 AI 编码 CLI 的工作流层:技能、知识库、交付检查、更安全的配置写入,以及可恢复的执行流程。**
|
|
10
10
|
|
|
11
|
-
[](./package.json)
|
|
12
12
|
[](https://www.npmjs.com/package/helloagents)
|
|
13
13
|
[](./package.json)
|
|
14
14
|
[](./skills)
|
|
@@ -189,6 +189,8 @@ HelloAGENTS 现在只从 `state_path` 解析当前状态文件:
|
|
|
189
189
|
|
|
190
190
|
`<workspace>` 是当前 Git 分支、detached HEAD 的 `detached-<sha>`,或非 Git 项目的 `workspace`。`.helloagents/sessions/active.json` 只记录当前活跃会话索引。
|
|
191
191
|
|
|
192
|
+
对于项目本地会话目录,HelloAGENTS 现在只使用稳定宿主标识,如 `sessionId`、`conversationId`、`threadId` 或 `HELLOAGENTS_NOTIFY_SESSION_ID`。它不再使用 `WT_SESSION`、`TERM_SESSION_ID`、`WINDOWID` 这类终端或窗口标识去额外生成项目会话目录。
|
|
193
|
+
|
|
192
194
|
`STATE.md` 只记录当前工作流做到哪里,不承担所有对话的统一记忆。Codex `/goal` 也不替代 `state_path`、`turn-state` 或本地证据文件;它只负责 Codex 侧的长程续跑。
|
|
193
195
|
|
|
194
196
|
### 6)验证与交付证据
|
|
@@ -206,6 +208,9 @@ HelloAGENTS 不把“命令通过”和“任务完成”简单画等号。交
|
|
|
206
208
|
- 仅用于 Codex 原生收尾去重的 `~/.codex/.helloagents/notify-state.json`
|
|
207
209
|
|
|
208
210
|
`turn-state`、路由上下文和 artifact 索引都写进 `STATE.md` 的元数据,不再单独生成 `capsule.json`。`events.jsonl` 改为可选 trace 输出,默认不写。
|
|
211
|
+
项目本地 `STATE.md` 现在会更晚创建;旧版残留的项目根 `.helloagents/artifacts/*.log` 也会自动清理,不再继续充当第二套历史系统。
|
|
212
|
+
|
|
213
|
+
标准运行态证据和临时运行态现在默认 72 小时过期。只有工作流明确需要的长程 Codex goal 链路,才继续保留 720 小时上限。
|
|
209
214
|
|
|
210
215
|
交付门控、守卫和 QA gate 提示使用执行性表述,例如处理路径、收尾动作和视觉验收动作。阻塞流程会说明下一步要做什么,而不是把可执行步骤写成泛化建议。最终回复还会强制只保留一个 HelloAGENTS 外层块,避免同一条回复重复输出完成标题。
|
|
211
216
|
这个外层格式现在只保留给直接面向最终用户的终局交付。中间汇报、委派任务结果和子代理回复都保持自然输出;子代理结束钩子也会拦截错误的外层收尾格式。
|
|
@@ -304,7 +309,7 @@ helloagents codex goals enable
|
|
|
304
309
|
|
|
305
310
|
### npm 和一键脚本入口
|
|
306
311
|
|
|
307
|
-
当你不想依赖更新过程中的 `helloagents` 可执行文件时,用 npm 或一键脚本。`HELLOAGENTS=目标[:模式]` 中,目标支持 `all`、`claude`、`gemini`、`codex`;模式支持 `standby`、`global`。用于安装时,省略模式按 `standby` 处理;用于更新、清理、卸载和切换分支时,省略模式会原样下传,让 HelloAGENTS 先复用该 CLI
|
|
312
|
+
当你不想依赖更新过程中的 `helloagents` 可执行文件时,用 npm 或一键脚本。`HELLOAGENTS=目标[:模式]` 中,目标支持 `all`、`claude`、`gemini`、`codex`;模式支持 `standby`、`global`。用于安装时,省略模式按 `standby` 处理;用于更新、清理、卸载和切换分支时,省略模式会原样下传,让 HelloAGENTS 先复用该 CLI 已记录或检测到的模式。如果未提供 `HELLOAGENTS`,一键安装脚本现在会保持“只装包/只升级包”的默认语义,不会自动部署任何宿主 CLI。若要安装自定义 tarball 或包规格,用 `HELLOAGENTS_PACKAGE`,不要写 `HELLOAGENTS_BRANCH`。对于已经装好的包,如需确保宿主一定刷新,优先在包命令后显式执行一次 `npm explore -g helloagents -- npm run sync-hosts -- ...`。
|
|
308
313
|
|
|
309
314
|
宿主配置使用稳定的 `helloagents-js` 入口和运行根目录 `~/.helloagents/helloagents`,Node 全局包路径变化不会破坏受管 hooks 或 Codex `notify`。Codex hooks 使用独立 `~/.codex/hooks.json`,不把大段配置写入 `config.toml`;Codex 全局插件根目录和插件缓存也会回链到这个稳定运行根目录。
|
|
310
315
|
|
|
@@ -405,7 +410,7 @@ $env:HELLOAGENTS="codex:standby"; $env:HELLOAGENTS_ACTION="cleanup"; irm https:/
|
|
|
405
410
|
$env:HELLOAGENTS="gemini"; $env:HELLOAGENTS_ACTION="uninstall"; irm https://raw.githubusercontent.com/hellowind777/helloagents/main/install.ps1 | iex
|
|
406
411
|
```
|
|
407
412
|
|
|
408
|
-
Shell 和 PowerShell 一键脚本现在都会先解析一次 `HELLOAGENTS
|
|
413
|
+
Shell 和 PowerShell 一键脚本现在都会先解析一次 `HELLOAGENTS`;未指定目标时保持普通包安装/升级语义;在更新、切分支和卸载前清掉生命周期环境变量,然后只走一条显式同步或清理链路。
|
|
409
414
|
|
|
410
415
|
### 分支切换
|
|
411
416
|
|
|
@@ -442,12 +447,12 @@ npm uninstall -g helloagents
|
|
|
442
447
|
| Gemini CLI | 原生扩展安装 | 由 Gemini 扩展系统管理 |
|
|
443
448
|
| Codex CLI | 原生本地插件流程 | `~/.agents/plugins/marketplace.json`、`~/plugins/helloagents/ -> ~/.helloagents/helloagents`、`~/.codex/plugins/cache/local-plugins/helloagents/local/ -> ~/.helloagents/helloagents`、`~/.codex/config.toml`、`~/.codex/hooks.json`、`~/.codex/helloagents -> ~/.helloagents/helloagents` |
|
|
444
449
|
|
|
445
|
-
全局模式下,HelloAGENTS
|
|
450
|
+
全局模式下,HelloAGENTS 会自动尝试宿主原生命令。对 Claude Code,marketplace 应使用 Git URL 添加,这样插件安装阶段会继续走 HTTPS,不会落回 SSH-only clone。若宿主命令不可用,再手动执行:
|
|
446
451
|
|
|
447
452
|
```text
|
|
448
|
-
/plugin marketplace add hellowind777/helloagents
|
|
453
|
+
/plugin marketplace add https://github.com/hellowind777/helloagents.git
|
|
449
454
|
/plugin install helloagents@helloagents
|
|
450
|
-
|
|
455
|
+
helloagents install gemini --global
|
|
451
456
|
```
|
|
452
457
|
|
|
453
458
|
Claude Code 会自动尝试等价的 `claude plugin marketplace add ...` 和 `claude plugin install ...` 命令。marketplace 名称和插件名称都是 `helloagents`,所以安装目标是 `helloagents@helloagents`。全局安装后需要重启宿主 CLI。
|
package/gemini-extension.json
CHANGED
package/install.ps1
CHANGED
|
@@ -16,6 +16,7 @@ $Mode = if ($env:HELLOAGENTS_MODE) { $env:HELLOAGENTS_MODE } else { "" }
|
|
|
16
16
|
$Branch = if ($env:HELLOAGENTS_BRANCH) { $env:HELLOAGENTS_BRANCH } else { "" }
|
|
17
17
|
$Package = if ($env:HELLOAGENTS_PACKAGE) { $env:HELLOAGENTS_PACKAGE } else { "" }
|
|
18
18
|
$HasExplicitPackage = [bool]$Package
|
|
19
|
+
$HasExplicitTarget = $false
|
|
19
20
|
|
|
20
21
|
if ($env:HELLOAGENTS) {
|
|
21
22
|
$Parts = $env:HELLOAGENTS.Split(":", 2)
|
|
@@ -26,6 +27,8 @@ if ($env:HELLOAGENTS) {
|
|
|
26
27
|
if (-not $Mode -and $Parts.Count -gt 1) { $Mode = $Parts[1] }
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
$HasExplicitTarget = [bool]$Target
|
|
31
|
+
|
|
29
32
|
if (-not $Target) { $Target = "all" }
|
|
30
33
|
$Target = $Target.ToLowerInvariant()
|
|
31
34
|
if ($Mode) { $Mode = $Mode.ToLowerInvariant() }
|
|
@@ -107,7 +110,9 @@ function Uninstall-Hosts {
|
|
|
107
110
|
|
|
108
111
|
switch ($Action) {
|
|
109
112
|
"install" {
|
|
110
|
-
|
|
113
|
+
if ($HasExplicitTarget) {
|
|
114
|
+
Enable-PostinstallDeploy
|
|
115
|
+
}
|
|
111
116
|
Invoke-Npm -NpmArgs @("install", "-g", $Package)
|
|
112
117
|
}
|
|
113
118
|
"update" {
|
package/install.sh
CHANGED
|
@@ -17,6 +17,7 @@ MODE="${HELLOAGENTS_MODE:-}"
|
|
|
17
17
|
BRANCH="${HELLOAGENTS_BRANCH:-}"
|
|
18
18
|
PACKAGE="${HELLOAGENTS_PACKAGE:-}"
|
|
19
19
|
HAS_EXPLICIT_PACKAGE=0
|
|
20
|
+
HAS_EXPLICIT_TARGET=0
|
|
20
21
|
if [ -n "$PACKAGE" ]; then
|
|
21
22
|
HAS_EXPLICIT_PACKAGE=1
|
|
22
23
|
fi
|
|
@@ -35,6 +36,10 @@ if [ -n "${HELLOAGENTS:-}" ]; then
|
|
|
35
36
|
MODE="${MODE:-$SPEC_MODE}"
|
|
36
37
|
fi
|
|
37
38
|
|
|
39
|
+
if [ -n "$TARGET" ]; then
|
|
40
|
+
HAS_EXPLICIT_TARGET=1
|
|
41
|
+
fi
|
|
42
|
+
|
|
38
43
|
TARGET="${TARGET:-all}"
|
|
39
44
|
TARGET="$(printf '%s' "$TARGET" | tr '[:upper:]' '[:lower:]')"
|
|
40
45
|
MODE="$(printf '%s' "$MODE" | tr '[:upper:]' '[:lower:]')"
|
|
@@ -128,7 +133,9 @@ enable_postinstall_deploy() {
|
|
|
128
133
|
|
|
129
134
|
case "$ACTION" in
|
|
130
135
|
install)
|
|
131
|
-
|
|
136
|
+
if [ "$HAS_EXPLICIT_TARGET" -eq 1 ]; then
|
|
137
|
+
enable_postinstall_deploy
|
|
138
|
+
fi
|
|
132
139
|
npm install -g "$PACKAGE"
|
|
133
140
|
;;
|
|
134
141
|
update)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "helloagents",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.37",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "HelloAGENTS — The orchestration kernel that makes any AI CLI smarter. Adds intelligent routing, unified QA gates, safety guards, and notifications.",
|
|
6
6
|
"author": "HelloWind",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process'
|
|
2
2
|
import { existsSync, realpathSync } from 'node:fs'
|
|
3
|
+
import { platform } from 'node:os'
|
|
3
4
|
import { join } from 'node:path'
|
|
4
5
|
|
|
5
6
|
import { CODEX_MARKETPLACE_NAME, CODEX_PLUGIN_CONFIG_HEADER, CODEX_PLUGIN_NAME } from './cli-codex.mjs'
|
|
@@ -147,8 +148,9 @@ function summarizeNativeCodexDoctorOutput(payload = {}) {
|
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
function inspectNativeCodexDoctor(runtime) {
|
|
151
|
+
const command = platform() === 'win32' ? 'codex.cmd' : 'codex'
|
|
150
152
|
try {
|
|
151
|
-
const result = spawnSync(
|
|
153
|
+
const result = spawnSync(command, ['doctor', '--json'], {
|
|
152
154
|
cwd: process.cwd(),
|
|
153
155
|
env: {
|
|
154
156
|
...process.env,
|
|
@@ -158,6 +160,7 @@ function inspectNativeCodexDoctor(runtime) {
|
|
|
158
160
|
},
|
|
159
161
|
encoding: 'utf-8',
|
|
160
162
|
timeout: 20_000,
|
|
163
|
+
shell: platform() === 'win32',
|
|
161
164
|
windowsHide: true,
|
|
162
165
|
})
|
|
163
166
|
|
package/scripts/cli-doctor.mjs
CHANGED
|
@@ -106,8 +106,8 @@ function suggestDoctorFix(host, status, trackedMode) {
|
|
|
106
106
|
return `helloagents update ${host}${trackedMode && trackedMode !== 'none' ? ` --${trackedMode}` : ''}`
|
|
107
107
|
}
|
|
108
108
|
if (status === 'manual-plugin') {
|
|
109
|
-
if (host === 'claude') return '/plugin marketplace add hellowind777/helloagents; /plugin install helloagents@helloagents'
|
|
110
|
-
if (host === 'gemini') return '
|
|
109
|
+
if (host === 'claude') return '/plugin marketplace add https://github.com/hellowind777/helloagents.git; /plugin install helloagents@helloagents'
|
|
110
|
+
if (host === 'gemini') return 'helloagents install gemini --global'
|
|
111
111
|
}
|
|
112
112
|
if (status === 'not-installed') {
|
|
113
113
|
return `helloagents install ${host} --standby`
|
|
@@ -176,7 +176,7 @@ function inspectGeminiDoctor(settings) {
|
|
|
176
176
|
const detectedMode = normalizeDoctorMode(runtime.detectHostMode(host))
|
|
177
177
|
const geminiDir = join(runtime.home, '.gemini')
|
|
178
178
|
const geminiSettings = safeJson(join(geminiDir, 'settings.json')) || {}
|
|
179
|
-
const expectedHooks = readExpectedHooks('hooks.json', '${extensionPath}')
|
|
179
|
+
const expectedHooks = readExpectedHooks('hooks-gemini.json', '${extensionPath}')
|
|
180
180
|
const checks = {
|
|
181
181
|
carrierMarker: (safeRead(join(geminiDir, 'GEMINI.md')) || '').includes('HELLOAGENTS_START'),
|
|
182
182
|
carrierContentMatch: extractManagedCarrierContent(join(geminiDir, 'GEMINI.md'))
|
package/scripts/cli-hosts.mjs
CHANGED
|
@@ -65,7 +65,7 @@ export function installGeminiStandby(home, pkgRoot) {
|
|
|
65
65
|
createLink(pkgRoot, join(geminiDir, 'helloagents'));
|
|
66
66
|
|
|
67
67
|
const settingsPath = join(geminiDir, 'settings.json');
|
|
68
|
-
const hooksData = loadHooksWithCliEntry(pkgRoot, 'hooks.json', '${extensionPath}');
|
|
68
|
+
const hooksData = loadHooksWithCliEntry(pkgRoot, 'hooks-gemini.json', '${extensionPath}');
|
|
69
69
|
if (hooksData) mergeSettingsHooks(settingsPath, hooksData);
|
|
70
70
|
|
|
71
71
|
return true;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process'
|
|
2
|
+
import { platform } from 'node:os'
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
installClaudeStandby,
|
|
@@ -17,23 +18,50 @@ import { getHostLabel } from './cli-host-detect.mjs'
|
|
|
17
18
|
|
|
18
19
|
const CLAUDE_COMMAND = process.env.HELLOAGENTS_CLAUDE_CMD || 'claude'
|
|
19
20
|
const GEMINI_COMMAND = process.env.HELLOAGENTS_GEMINI_CMD || 'gemini'
|
|
20
|
-
const CLAUDE_MARKETPLACE = 'hellowind777/helloagents'
|
|
21
|
+
const CLAUDE_MARKETPLACE = 'https://github.com/hellowind777/helloagents.git'
|
|
21
22
|
const CLAUDE_PLUGIN = 'helloagents@helloagents'
|
|
22
|
-
|
|
23
|
+
|
|
24
|
+
function normalizeCommand(command = '') {
|
|
25
|
+
return String(command || '').trim()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function commandCandidates(command = '') {
|
|
29
|
+
const normalized = normalizeCommand(command)
|
|
30
|
+
if (!normalized) return []
|
|
31
|
+
|
|
32
|
+
if (platform() !== 'win32') return [normalized]
|
|
33
|
+
|
|
34
|
+
const candidates = new Set([normalized])
|
|
35
|
+
if (!/\.(cmd|bat|exe|ps1)$/i.test(normalized)) {
|
|
36
|
+
candidates.add(`${normalized}.cmd`)
|
|
37
|
+
candidates.add(`${normalized}.exe`)
|
|
38
|
+
}
|
|
39
|
+
return [...candidates]
|
|
40
|
+
}
|
|
23
41
|
|
|
24
42
|
function runHostCommand(command, args) {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
43
|
+
const attempts = commandCandidates(command)
|
|
44
|
+
let lastResult = null
|
|
45
|
+
|
|
46
|
+
for (const candidate of attempts) {
|
|
47
|
+
const needsShell = process.platform === 'win32' && /\.(cmd|bat)$/i.test(candidate)
|
|
48
|
+
const result = spawnSync(candidate, args, {
|
|
49
|
+
encoding: 'utf-8',
|
|
50
|
+
errors: 'replace',
|
|
51
|
+
shell: needsShell,
|
|
52
|
+
windowsHide: true,
|
|
53
|
+
})
|
|
54
|
+
lastResult = result
|
|
55
|
+
if (!result.error || !['ENOENT', 'EINVAL'].includes(result.error.code)) {
|
|
56
|
+
break
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const errorMessage = lastResult?.error?.message || ''
|
|
33
61
|
return {
|
|
34
|
-
ok:
|
|
35
|
-
missing:
|
|
36
|
-
output: `${
|
|
62
|
+
ok: lastResult?.status === 0,
|
|
63
|
+
missing: ['ENOENT', 'EINVAL'].includes(lastResult?.error?.code || ''),
|
|
64
|
+
output: `${lastResult?.stdout || ''}${lastResult?.stderr || ''}${errorMessage}`.trim(),
|
|
37
65
|
}
|
|
38
66
|
}
|
|
39
67
|
|
|
@@ -60,8 +88,8 @@ function installClaudeGlobalPlugin() {
|
|
|
60
88
|
return { ok: install.ok, output: install.output || add.output }
|
|
61
89
|
}
|
|
62
90
|
|
|
63
|
-
function installGeminiGlobalExtension() {
|
|
64
|
-
return runHostCommand(GEMINI_COMMAND, ['extensions', '
|
|
91
|
+
function installGeminiGlobalExtension(runtimeRoot) {
|
|
92
|
+
return runHostCommand(GEMINI_COMMAND, ['extensions', 'link', runtimeRoot])
|
|
65
93
|
}
|
|
66
94
|
|
|
67
95
|
function removeClaudeGlobalPlugin() {
|
|
@@ -150,18 +178,18 @@ function installHostGlobal(runtime, host) {
|
|
|
150
178
|
installClaudeGlobalPlugin(),
|
|
151
179
|
'已自动安装 Claude Code 插件;重启 Claude Code 后生效',
|
|
152
180
|
'Claude Code plugin installed automatically; restart Claude Code to apply',
|
|
153
|
-
'Claude Code 插件自动安装失败,请在 Claude Code 中执行: /plugin marketplace add hellowind777/helloagents;/plugin install helloagents@helloagents',
|
|
154
|
-
'Claude Code plugin auto-install failed. Run inside Claude Code: /plugin marketplace add hellowind777/helloagents; /plugin install helloagents@helloagents',
|
|
181
|
+
'Claude Code 插件自动安装失败,请在 Claude Code 中执行: /plugin marketplace add https://github.com/hellowind777/helloagents.git;/plugin install helloagents@helloagents',
|
|
182
|
+
'Claude Code plugin auto-install failed. Run inside Claude Code: /plugin marketplace add https://github.com/hellowind777/helloagents.git; /plugin install helloagents@helloagents',
|
|
155
183
|
)
|
|
156
184
|
}
|
|
157
185
|
if (host === 'gemini') {
|
|
158
186
|
uninstallGeminiStandby(runtime.home)
|
|
159
187
|
return buildNativeResult(
|
|
160
|
-
installGeminiGlobalExtension(),
|
|
188
|
+
installGeminiGlobalExtension(runtime.pkgRoot),
|
|
161
189
|
'已自动安装 Gemini CLI 扩展;重启 Gemini CLI 后生效',
|
|
162
190
|
'Gemini CLI extension installed automatically; restart Gemini CLI to apply',
|
|
163
|
-
|
|
164
|
-
|
|
191
|
+
`Gemini CLI 扩展自动安装失败,请手动执行: gemini extensions link ${runtime.pkgRoot}`,
|
|
192
|
+
`Gemini CLI extension auto-install failed. Run manually: gemini extensions link ${runtime.pkgRoot}`,
|
|
165
193
|
)
|
|
166
194
|
}
|
|
167
195
|
uninstallCodexStandby(runtime.home)
|
package/scripts/cli-messages.mjs
CHANGED
|
@@ -21,9 +21,9 @@ function codexGlobalStatus({ home, msg }) {
|
|
|
21
21
|
|
|
22
22
|
function pluginCommands() {
|
|
23
23
|
return [
|
|
24
|
-
' Claude Code: /plugin marketplace add hellowind777/helloagents',
|
|
24
|
+
' Claude Code: /plugin marketplace add https://github.com/hellowind777/helloagents.git',
|
|
25
25
|
' /plugin install helloagents@helloagents',
|
|
26
|
-
' Gemini CLI:
|
|
26
|
+
' Gemini CLI: helloagents install gemini --global',
|
|
27
27
|
].join('\n')
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdtempSync, realpathSync, renameSync } from 'node:fs'
|
|
1
|
+
import { copyFileSync, existsSync, mkdtempSync, realpathSync, renameSync } from 'node:fs'
|
|
2
2
|
import { dirname, join, resolve } from 'node:path'
|
|
3
3
|
|
|
4
4
|
import { copyEntries, ensureDir, removeIfExists } from './cli-utils.mjs'
|
|
@@ -63,6 +63,13 @@ function retryTransientFs(operation) {
|
|
|
63
63
|
throw lastError
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
function materializeGeminiHooks(root) {
|
|
67
|
+
const source = join(root, 'hooks', 'hooks-gemini.json')
|
|
68
|
+
const target = join(root, 'hooks', 'hooks.json')
|
|
69
|
+
if (!existsSync(source)) return
|
|
70
|
+
copyFileSync(source, target)
|
|
71
|
+
}
|
|
72
|
+
|
|
66
73
|
/** Sync package runtime files into the stable root without copying repo-only files. */
|
|
67
74
|
export function syncRuntimeRoot(sourceRoot, runtimeRoot) {
|
|
68
75
|
const source = resolve(sourceRoot)
|
|
@@ -77,6 +84,7 @@ export function syncRuntimeRoot(sourceRoot, runtimeRoot) {
|
|
|
77
84
|
|
|
78
85
|
try {
|
|
79
86
|
copyEntries(source, staging, RUNTIME_ROOT_ENTRIES)
|
|
87
|
+
materializeGeminiHooks(staging)
|
|
80
88
|
retryTransientFs(() => {
|
|
81
89
|
removeIfExists(target)
|
|
82
90
|
renameSync(staging, target)
|
package/scripts/notify-sound.mjs
CHANGED
|
@@ -24,7 +24,8 @@ export function buildWindowsSoundCommand(filePath = '') {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
function playWindows(filePath) {
|
|
27
|
-
const
|
|
27
|
+
const command = process.platform === 'win32' ? 'powershell.exe' : 'powershell'
|
|
28
|
+
const result = spawnSync(command, [
|
|
28
29
|
'-NoProfile',
|
|
29
30
|
'-c',
|
|
30
31
|
buildWindowsSoundCommand(filePath),
|
|
@@ -10,9 +10,11 @@ import {
|
|
|
10
10
|
writeJsonFileAtomic,
|
|
11
11
|
} from './runtime-scope.mjs'
|
|
12
12
|
import { LONG_RUNNING_TTL_MS } from './runtime-ttl.mjs'
|
|
13
|
+
import { readStateDocument } from './state-document.mjs'
|
|
13
14
|
|
|
14
15
|
export const PROJECT_SESSION_CLEANUP_COOLDOWN_MS = 10 * 60 * 1000
|
|
15
16
|
export const PROJECT_SESSION_MAX_AGE_MS = LONG_RUNNING_TTL_MS
|
|
17
|
+
const AUTO_CREATED_STATE_MARKER = '由运行时自动创建;后续按实际任务重写'
|
|
16
18
|
|
|
17
19
|
function removePath(filePath, result, bucket) {
|
|
18
20
|
try {
|
|
@@ -23,6 +25,10 @@ function removePath(filePath, result, bucket) {
|
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
|
|
28
|
+
function isDebugLog(entryName = '') {
|
|
29
|
+
return /\.log$/i.test(entryName)
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
function isDirectoryEmptyRecursive(dirPath) {
|
|
27
33
|
const entries = readdirSync(dirPath, { withFileTypes: true })
|
|
28
34
|
if (entries.length === 0) return true
|
|
@@ -55,6 +61,15 @@ function hasStateSnapshot(sessionDir) {
|
|
|
55
61
|
return existsSync(join(sessionDir, 'STATE.md'))
|
|
56
62
|
}
|
|
57
63
|
|
|
64
|
+
function isAutoCreatedSeedSession(sessionDir) {
|
|
65
|
+
const statePath = join(sessionDir, 'STATE.md')
|
|
66
|
+
if (!existsSync(statePath)) return false
|
|
67
|
+
|
|
68
|
+
const { metadata, body } = readStateDocument(statePath)
|
|
69
|
+
if (metadata && typeof metadata === 'object' && Object.keys(metadata).length > 0) return false
|
|
70
|
+
return String(body || '').includes(AUTO_CREATED_STATE_MARKER)
|
|
71
|
+
}
|
|
72
|
+
|
|
58
73
|
function readSessionStateMtimeMs(sessionDir) {
|
|
59
74
|
try {
|
|
60
75
|
return statSync(join(sessionDir, 'STATE.md')).mtimeMs
|
|
@@ -79,6 +94,32 @@ function cleanupTransientSessionTemps(sessionsDir, result) {
|
|
|
79
94
|
}
|
|
80
95
|
}
|
|
81
96
|
|
|
97
|
+
function cleanupLegacyProjectArtifacts(activationDir, result) {
|
|
98
|
+
const artifactsDir = join(activationDir, 'artifacts')
|
|
99
|
+
if (!existsSync(artifactsDir)) return
|
|
100
|
+
|
|
101
|
+
let removableEntries = []
|
|
102
|
+
try {
|
|
103
|
+
removableEntries = readdirSync(artifactsDir, { withFileTypes: true })
|
|
104
|
+
} catch (error) {
|
|
105
|
+
result.errors.push(`${artifactsDir}: ${error.message}`)
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (const entry of removableEntries) {
|
|
110
|
+
if (!entry.isFile() || !isDebugLog(entry.name)) continue
|
|
111
|
+
removePath(join(artifactsDir, entry.name), result, 'removedLegacyArtifacts')
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
if (isDirectoryEmptyRecursive(artifactsDir)) {
|
|
116
|
+
removePath(artifactsDir, result, 'removedLegacyArtifacts')
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
result.errors.push(`${artifactsDir}: ${error.message}`)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
82
123
|
export function cleanupProjectSessions(cwd, { now = Date.now(), minIntervalMs = 0, maxAgeMs = PROJECT_SESSION_MAX_AGE_MS } = {}) {
|
|
83
124
|
const projectRoot = getProjectRoot(cwd)
|
|
84
125
|
const activationDir = getProjectActivationDir(projectRoot)
|
|
@@ -90,7 +131,9 @@ export function cleanupProjectSessions(cwd, { now = Date.now(), minIntervalMs =
|
|
|
90
131
|
removedEmptyDirs: [],
|
|
91
132
|
removedInactiveDirs: [],
|
|
92
133
|
removedNoStateDirs: [],
|
|
134
|
+
removedSeedDirs: [],
|
|
93
135
|
removedTempFiles: [],
|
|
136
|
+
removedLegacyArtifacts: [],
|
|
94
137
|
errors: [],
|
|
95
138
|
skipped: false,
|
|
96
139
|
}
|
|
@@ -109,6 +152,7 @@ export function cleanupProjectSessions(cwd, { now = Date.now(), minIntervalMs =
|
|
|
109
152
|
} catch (error) {
|
|
110
153
|
result.errors.push(`${sessionsDir}: ${error.message}`)
|
|
111
154
|
}
|
|
155
|
+
cleanupLegacyProjectArtifacts(activationDir, result)
|
|
112
156
|
|
|
113
157
|
for (const workspaceEntry of readdirSync(sessionsDir, { withFileTypes: true })) {
|
|
114
158
|
if (!workspaceEntry.isDirectory()) continue
|
|
@@ -124,6 +168,8 @@ export function cleanupProjectSessions(cwd, { now = Date.now(), minIntervalMs =
|
|
|
124
168
|
removePath(sessionDir, result, 'removedEmptyDirs')
|
|
125
169
|
} else if (!hasStateSnapshot(sessionDir)) {
|
|
126
170
|
removePath(sessionDir, result, 'removedNoStateDirs')
|
|
171
|
+
} else if (isAutoCreatedSeedSession(sessionDir)) {
|
|
172
|
+
removePath(sessionDir, result, 'removedSeedDirs')
|
|
127
173
|
} else if (isStaleStateSession(sessionDir, now, maxAgeMs)) {
|
|
128
174
|
removePath(sessionDir, result, 'removedInactiveDirs')
|
|
129
175
|
}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
readSessionArtifact,
|
|
8
8
|
writeSessionArtifact,
|
|
9
9
|
} from './session-capsule.mjs'
|
|
10
|
-
import { EVIDENCE_MAX_AGE_MS, LONG_RUNNING_TTL_HOURS } from './runtime-ttl.mjs'
|
|
10
|
+
import { EVIDENCE_MAX_AGE_MS, LONG_RUNNING_TTL_HOURS, STANDARD_RUNTIME_TTL_HOURS } from './runtime-ttl.mjs'
|
|
11
11
|
|
|
12
12
|
export { EVIDENCE_MAX_AGE_MS }
|
|
13
13
|
|
|
@@ -87,7 +87,7 @@ export function validateEvidenceTimestamp(evidence, now, label) {
|
|
|
87
87
|
required: true,
|
|
88
88
|
status: 'stale-time',
|
|
89
89
|
evidence,
|
|
90
|
-
details: [`${label}超过 ${LONG_RUNNING_TTL_HOURS}
|
|
90
|
+
details: [`${label}超过 ${STANDARD_RUNTIME_TTL_HOURS} 小时(长任务上限:${LONG_RUNNING_TTL_HOURS} 小时)`],
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
return null
|
|
@@ -4,7 +4,7 @@ import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync,
|
|
|
4
4
|
import { dirname, join, normalize, resolve } from 'node:path'
|
|
5
5
|
import { homedir } from 'node:os'
|
|
6
6
|
|
|
7
|
-
import { resolveSessionToken } from './session-token.mjs'
|
|
7
|
+
import { resolveProjectSessionToken, resolveSessionToken } from './session-token.mjs'
|
|
8
8
|
import { USER_RUNTIME_MAX_AGE_MS } from './runtime-ttl.mjs'
|
|
9
9
|
import { cleanupUserRuntimeRoot, getUserRuntimeRoot } from './runtime-user-cleanup.mjs'
|
|
10
10
|
import { FULL_CARRIER_PROFILE_MARKER } from './cli-utils.mjs'
|
|
@@ -323,21 +323,14 @@ function findProjectActivationDir(cwd) {
|
|
|
323
323
|
|
|
324
324
|
function resolvePayloadSessionToken(payload = {}) {
|
|
325
325
|
if (payload?._helloagentsSessionAlias) return ''
|
|
326
|
-
return
|
|
326
|
+
return resolveProjectSessionToken({
|
|
327
327
|
payload,
|
|
328
328
|
env: {},
|
|
329
|
-
ppid: 0,
|
|
330
|
-
allowPpidFallback: false,
|
|
331
329
|
})
|
|
332
330
|
}
|
|
333
331
|
|
|
334
332
|
function resolveEnvSessionToken(env = process.env) {
|
|
335
|
-
return
|
|
336
|
-
payload: {},
|
|
337
|
-
env,
|
|
338
|
-
ppid: 0,
|
|
339
|
-
allowPpidFallback: false,
|
|
340
|
-
})
|
|
333
|
+
return resolveProjectSessionToken({ payload: {}, env })
|
|
341
334
|
}
|
|
342
335
|
|
|
343
336
|
function resolveTransientSessionToken({ payload = {}, env = process.env, ppid = process.ppid } = {}) {
|
|
@@ -437,11 +430,21 @@ function chooseProjectSession({ payload, env, activationDir, projectRoot, worksp
|
|
|
437
430
|
return { session: DEFAULT_STATE_SESSION_TOKEN, sessionMode: 'default' }
|
|
438
431
|
}
|
|
439
432
|
|
|
433
|
+
function removeLegacyProjectArtifacts(activationDir) {
|
|
434
|
+
if (!activationDir) return
|
|
435
|
+
const artifactsDir = join(activationDir, PROJECT_ARTIFACTS_DIR_NAME)
|
|
436
|
+
if (!existsSync(artifactsDir)) return
|
|
437
|
+
try {
|
|
438
|
+
rmSync(artifactsDir, { recursive: true, force: true })
|
|
439
|
+
} catch {}
|
|
440
|
+
}
|
|
441
|
+
|
|
440
442
|
export function getProjectSessionScope(cwd, options = {}) {
|
|
441
443
|
const normalizedCwd = normalizePath(cwd || process.cwd())
|
|
442
444
|
const projectRoot = getProjectRoot(normalizedCwd)
|
|
443
445
|
const { payload = {}, env = process.env } = normalizeRuntimeOptions(options)
|
|
444
446
|
const activationDir = getProjectActivationDir(projectRoot)
|
|
447
|
+
removeLegacyProjectArtifacts(activationDir)
|
|
445
448
|
const workspace = resolveWorkspaceName(projectRoot)
|
|
446
449
|
const { session, sessionMode } = chooseProjectSession({
|
|
447
450
|
payload,
|
package/scripts/runtime-ttl.mjs
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
export const LONG_RUNNING_TTL_HOURS = 720
|
|
2
2
|
export const LONG_RUNNING_TTL_MS = LONG_RUNNING_TTL_HOURS * 60 * 60 * 1000
|
|
3
3
|
|
|
4
|
-
export const
|
|
5
|
-
export const
|
|
6
|
-
|
|
7
|
-
export const
|
|
4
|
+
export const STANDARD_RUNTIME_TTL_HOURS = 72
|
|
5
|
+
export const STANDARD_RUNTIME_TTL_MS = STANDARD_RUNTIME_TTL_HOURS * 60 * 60 * 1000
|
|
6
|
+
|
|
7
|
+
export const ROUTE_CONTEXT_TTL_MS = STANDARD_RUNTIME_TTL_MS
|
|
8
|
+
export const TURN_STATE_TTL_MS = STANDARD_RUNTIME_TTL_MS
|
|
9
|
+
export const EVIDENCE_MAX_AGE_MS = STANDARD_RUNTIME_TTL_MS
|
|
10
|
+
export const USER_RUNTIME_MAX_AGE_MS = STANDARD_RUNTIME_TTL_MS
|
|
@@ -68,6 +68,21 @@ function getScope(cwd, options = {}) {
|
|
|
68
68
|
return getRuntimeScope(cwd, normalizedOptions)
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
function shouldMaterializeSessionState(options = {}) {
|
|
72
|
+
const normalizedOptions = normalizeOptions(options)
|
|
73
|
+
if (normalizedOptions.ensureProjectLocal === true) return true
|
|
74
|
+
if (normalizedOptions.project === true) return true
|
|
75
|
+
if (normalizedOptions.traceEvents === true) return true
|
|
76
|
+
|
|
77
|
+
const payload = normalizedOptions.payload || {}
|
|
78
|
+
if (payload.traceEvents === true || payload._helloagentsTraceEvents === true) return true
|
|
79
|
+
|
|
80
|
+
const raw = String(normalizedOptions.env?.HELLOAGENTS_TRACE_EVENTS || process.env.HELLOAGENTS_TRACE_EVENTS || '')
|
|
81
|
+
.trim()
|
|
82
|
+
.toLowerCase()
|
|
83
|
+
return raw === '1' || raw === 'true' || raw === 'yes'
|
|
84
|
+
}
|
|
85
|
+
|
|
71
86
|
export function getSessionCapsulePath(cwd = process.cwd(), options = {}) {
|
|
72
87
|
return getScope(cwd, options).statePath
|
|
73
88
|
}
|
|
@@ -113,9 +128,10 @@ export function readSessionCapsule(cwd = process.cwd(), options = {}) {
|
|
|
113
128
|
export function writeSessionCapsule(cwd, capsule, options = {}) {
|
|
114
129
|
const normalizedOptions = normalizeOptions(options)
|
|
115
130
|
const scope = getScope(cwd, normalizedOptions)
|
|
131
|
+
const shouldMaterialize = shouldMaterializeSessionState(normalizedOptions)
|
|
116
132
|
const currentDocument = readStateDocument(scope.statePath)
|
|
117
133
|
const hasBody = Boolean(currentDocument.body && currentDocument.body.trim())
|
|
118
|
-
if (!hasBody &&
|
|
134
|
+
if (!hasBody && !shouldMaterialize && !existsSync(scope.statePath)) {
|
|
119
135
|
return {
|
|
120
136
|
...buildEmptyCapsule(scope),
|
|
121
137
|
...capsule,
|
|
@@ -129,6 +145,14 @@ export function writeSessionCapsule(cwd, capsule, options = {}) {
|
|
|
129
145
|
updatedAt: new Date().toISOString(),
|
|
130
146
|
}
|
|
131
147
|
}
|
|
148
|
+
if (!hasBody && shouldMaterialize && !existsSync(scope.statePath)) {
|
|
149
|
+
ensureProjectLocalRuntime(cwd, {
|
|
150
|
+
...normalizedOptions,
|
|
151
|
+
stateSeed: normalizedOptions.stateSeed && typeof normalizedOptions.stateSeed === 'object'
|
|
152
|
+
? normalizedOptions.stateSeed
|
|
153
|
+
: {},
|
|
154
|
+
})
|
|
155
|
+
}
|
|
132
156
|
const nextCapsule = {
|
|
133
157
|
...buildEmptyCapsule(scope),
|
|
134
158
|
...capsule,
|
|
@@ -17,6 +17,19 @@ const PAYLOAD_SESSION_KEYS = [
|
|
|
17
17
|
'tab',
|
|
18
18
|
]
|
|
19
19
|
|
|
20
|
+
const PROJECT_PAYLOAD_SESSION_KEYS = [
|
|
21
|
+
'sessionId',
|
|
22
|
+
'session_id',
|
|
23
|
+
'session',
|
|
24
|
+
'conversationId',
|
|
25
|
+
'conversation_id',
|
|
26
|
+
'conversation',
|
|
27
|
+
'threadId',
|
|
28
|
+
'thread_id',
|
|
29
|
+
'thread-id',
|
|
30
|
+
'thread',
|
|
31
|
+
]
|
|
32
|
+
|
|
20
33
|
const ENV_SESSION_KEYS = [
|
|
21
34
|
'HELLOAGENTS_NOTIFY_SESSION_ID',
|
|
22
35
|
'WT_SESSION',
|
|
@@ -28,6 +41,10 @@ const ENV_SESSION_KEYS = [
|
|
|
28
41
|
'TAB_ID',
|
|
29
42
|
]
|
|
30
43
|
|
|
44
|
+
const PROJECT_ENV_SESSION_KEYS = [
|
|
45
|
+
'HELLOAGENTS_NOTIFY_SESSION_ID',
|
|
46
|
+
]
|
|
47
|
+
|
|
31
48
|
function readStringCandidate(input, key) {
|
|
32
49
|
if (!input || typeof input !== 'object') return ''
|
|
33
50
|
const value = input[key]
|
|
@@ -50,23 +67,41 @@ export function sanitizeSessionToken(value = '') {
|
|
|
50
67
|
return cleaned.slice(0, 8)
|
|
51
68
|
}
|
|
52
69
|
|
|
70
|
+
function resolveTokenFromKeys(input, keys = []) {
|
|
71
|
+
for (const key of keys) {
|
|
72
|
+
const value = sanitizeSessionToken(readStringCandidate(input, key))
|
|
73
|
+
if (value) return value
|
|
74
|
+
}
|
|
75
|
+
return ''
|
|
76
|
+
}
|
|
77
|
+
|
|
53
78
|
export function resolveSessionToken({
|
|
54
79
|
payload = {},
|
|
55
80
|
env = process.env,
|
|
56
81
|
ppid = process.ppid,
|
|
57
82
|
allowPpidFallback = true,
|
|
58
83
|
} = {}) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (value) return value
|
|
62
|
-
}
|
|
84
|
+
const payloadToken = resolveTokenFromKeys(payload, PAYLOAD_SESSION_KEYS)
|
|
85
|
+
if (payloadToken) return payloadToken
|
|
63
86
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (value) return value
|
|
67
|
-
}
|
|
87
|
+
const envToken = resolveTokenFromKeys(env, ENV_SESSION_KEYS)
|
|
88
|
+
if (envToken) return envToken
|
|
68
89
|
|
|
69
90
|
return allowPpidFallback && ppid ? String(ppid) : ''
|
|
70
91
|
}
|
|
71
92
|
|
|
72
|
-
export {
|
|
93
|
+
export function resolveProjectSessionToken({
|
|
94
|
+
payload = {},
|
|
95
|
+
env = process.env,
|
|
96
|
+
} = {}) {
|
|
97
|
+
const payloadToken = resolveTokenFromKeys(payload, PROJECT_PAYLOAD_SESSION_KEYS)
|
|
98
|
+
if (payloadToken) return payloadToken
|
|
99
|
+
return resolveTokenFromKeys(env, PROJECT_ENV_SESSION_KEYS)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export {
|
|
103
|
+
ENV_SESSION_KEYS,
|
|
104
|
+
PAYLOAD_SESSION_KEYS,
|
|
105
|
+
PROJECT_ENV_SESSION_KEYS,
|
|
106
|
+
PROJECT_PAYLOAD_SESSION_KEYS,
|
|
107
|
+
}
|
|
File without changes
|