Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c091d83
feat(kiro-cli): add the Kiro CLI goal-mode host surface
Sep 7, 2026
63a0013
test(kiro-cli): cover the Kiro CLI host contract end to end
Sep 7, 2026
ec68639
docs(kiro-cli): document the Kiro CLI host surface
Sep 7, 2026
4877936
fix(slash-commands): quote front-matter scalars only when YAML needs it
Sep 7, 2026
d0a8e5d
fix(kiro-cli): pin the /goal surface to the probed binary
Sep 7, 2026
81fa8b5
feat(kiro-cli): serve Kiro CLI as a built-in Chat Agent
Sep 7, 2026
09a188d
fix(kiro-cli): resolve the host by identity across the control plane
Sep 7, 2026
68074fc
fix(ci): declare the yaml test dependency and refresh the packaged ch…
Sep 8, 2026
1e039ec
fix(ci): pin pyyaml in the stage2c hash-locked test requirements
Sep 8, 2026
a39c31f
fix(kiro-cli): resolve the host-authoritative KIRO_HOME
Sep 8, 2026
435818e
fix(chat): bound agent-family matching at the delimiter
Sep 8, 2026
2d29023
test(kiro-cli): cover the real host override and the identity bound
Sep 8, 2026
8fba5e4
fix(kiro-cli): derive the public activation output from the host facts
Sep 8, 2026
d05a504
fix(chat): release the ACP session before signalling the agent
Sep 8, 2026
01142c6
fix(dashboard): bound presented agent identity at the delimiter
Sep 8, 2026
61b8a07
test(kiro-cli): assert the public output contract, not just the resolver
Sep 8, 2026
48d7438
build(chat): regenerate the packaged bundle for the changed source
Sep 8, 2026
7183a88
fix(kiro-cli): render every actionable goal command from the composer
Sep 8, 2026
2a367c0
test(kiro-cli): make each command consumer sensitive to a dropped arg…
Sep 8, 2026
9aa3339
fix(kiro-cli): project only the contracted native goal arguments
Sep 9, 2026
24200ab
docs(kiro-cli): correct the documented goal syntax
Sep 9, 2026
3c288ff
test(kiro-cli): add a replayable goal command contract probe
Sep 9, 2026
68d3a51
test(kiro-cli): pin consumers to the contracted goal arguments
Sep 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ LoopX should reuse existing state rather than overwrite it. Keep `.loopx/`,
| Pi | Install the opt-in goal extension with `loopx slash-commands --install --surface pi`, then use `/loopx <task>` from a trusted Pi session. | Visible Pi goal extension gated by LoopX quota (`loopx_goal_activate` + `agent_settled` continuation) |
| ZCode | Install the skill facade with `loopx slash-commands --install --surface zcode`, then invoke the `$loopx` skill (or `/loopx <complex task>`) from a ZCode session in the project. | The ZCode session's own turn loop; every continuation enters through `quota should-run` |
| Antigravity CLI (agy) | Install the skill facade with `loopx slash-commands --install --surface agy`, then invoke the `loopx` skill (or `/loopx <complex task>`) from an `agy` session in the project. | The session's native `/goal` loop (audited until `<!-- GOAL_COMPLETE -->`) with `schedule` self-wakes while the session lives; the facade instructs every turn/wake to re-enter through `quota should-run` — advisory pacing, not a host-enforced gate |
| Kiro CLI | Install the skill facade with `loopx slash-commands --install --surface kiro-cli`, then run `/loopx <complex task>` from a `kiro-cli` session in the project. | The session's native `/goal --max <N> <task_body> Done when: <criteria>` loop, with the acceptance criteria stated inside the goal statement because the host derives them from it, bounded by the host's own iteration budget (default 5) and settled through the built-in `goal` completion contract; the facade instructs every turn and iteration to re-enter through `quota should-run` — advisory pacing, not a host-enforced gate |
| DeepSeek Harness (dsh) | Install the [native DSH plugin](packages/dsh-loopx-plugin/README.md), select the `loopx` skill, and describe the task. The [dsh goal-mode adapter](loopx/dsh_goal_mode/README.md) remains available for headless turns. | Native same-session continuation and GoalBar, or headless dsh segments; both remain gated by LoopX authority |
| Cursor, shell, or custom runner | Use the installer and `loopx doctor`; connect manually or call LoopX from your runner. | Your shell, scheduler, or runner |

Expand All @@ -303,7 +304,8 @@ The exact, copy-ready setup messages and host recovery paths live in
the [Codex App host command registry contract](docs/reference/protocols/codex-app-host-command-registry-v0.md),
the [Codex CLI packaged install path](docs/product/runtimes/codex-cli/codex-cli-packaged-install.md),
the [Claude Code adapter](loopx/claude_goal_mode/README.md), the
[KunlunCode native Goal adapter](loopx/kunluncode_goal_mode/README.md), or the
[KunlunCode native Goal adapter](loopx/kunluncode_goal_mode/README.md), the
[Kiro CLI goal-mode adapter](loopx/kiro_cli_goal_mode/README.md), or the
[DeepSeek Harness turn adapter](loopx/dsh_goal_mode/README.md).

See the [60-second DSH × LoopX Replan recording and reproducible
Expand Down
4 changes: 3 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ loopx start-goal --guided --project . --goal-text "你的长程目标"
| Pi | 用 `loopx slash-commands --install --surface pi` 安装 opt-in goal extension,然后在受信任的 Pi 会话里用 `/loopx <任务>`。 | 由 LoopX quota gate 的可见 Pi goal extension(`loopx_goal_activate` + `agent_settled` 续跑) |
| ZCode | 用 `loopx slash-commands --install --surface zcode` 安装 skill facade,然后在项目里的 ZCode 会话中调用 `$loopx` skill(或 `/loopx <复杂任务>`)。 | ZCode 会话自身的 turn loop;每次续跑都从 `quota should-run` 进入 |
| Antigravity CLI(agy) | 用 `loopx slash-commands --install --surface agy` 安装 skill facade,然后在项目里的 `agy` 会话中调用 `loopx` skill(或 `/loopx <复杂任务>`)。 | 会话原生 `/goal` 循环(审计至 `<!-- GOAL_COMPLETE -->`)加 `schedule` 自唤醒,随会话存活;facade 指示每次 turn/唤醒都先过 `quota should-run`——advisory 节流,非宿主强制 gate |
| Kiro CLI | 用 `loopx slash-commands --install --surface kiro-cli` 安装 skill facade,然后在项目里的 `kiro-cli` 会话中执行 `/loopx <复杂任务>`。 | 会话原生 `/goal --max <N> <任务> Done when: <验收条件>` 循环,验收条件写入目标语句本身(宿主由该语句推导验收标准),由宿主自己的迭代预算兜底(默认 5),并通过内置 `goal` 完成契约收口;facade 指示每次 turn 与迭代都先过 `quota should-run`——advisory 节流,非宿主强制 gate |
| DeepSeek Harness(dsh) | 安装 [DSH 原生 Plugin](packages/dsh-loopx-plugin/README.md),在技能选择器中点 `loopx`,然后直接描述任务;[dsh goal-mode adapter](loopx/dsh_goal_mode/README.md) 继续支持 headless turn。 | 原生同会话续跑与 GoalBar,或 headless dsh 工作段;两条路径都遵守 LoopX authority |
| Cursor、shell、自有 runner | 使用同一 installer 和 `loopx doctor`,再手动连接或由 runner 调用。 | 你的 shell、scheduler 或 runner |

Expand All @@ -273,7 +274,8 @@ loopx start-goal --guided --project . --goal-text "你的长程目标"
[Codex App host command registry](docs/reference/protocols/codex-app-host-command-registry-v0.md)、
[Codex CLI packaged install](docs/product/runtimes/codex-cli/codex-cli-packaged-install.md)和
[Claude Code adapter](loopx/claude_goal_mode/README.md)、
[KunlunCode 原生 Goal adapter](docs/guides/kunluncode-adapter.zh-CN.md),以及
[KunlunCode 原生 Goal adapter](docs/guides/kunluncode-adapter.zh-CN.md)、
[Kiro CLI goal-mode adapter](loopx/kiro_cli_goal_mode/README.md),以及
[DeepSeek Harness turn adapter](loopx/dsh_goal_mode/README.md)。

可查看 [60 秒 DSH × LoopX Replan 真实录屏和可复现
Expand Down
25 changes: 25 additions & 0 deletions apps/presentation/dashboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,31 @@ URL and opens the browser/PWA route, then exits without starting a second
server. The desktop shell reuses the same services in the opposite order, so
the browser/PWA and native entry points can be started in either order.

### Built-In Chat Agents

The Agent picker is served by `/api/chat/capabilities`, so its rows come from
the running LoopX process rather than from browser state. The built-ins are
Codex (app-server), Claude Code, Kiro CLI, and the direct Claude/OpenAI API
lanes; owner-registered ACP endpoints are appended after them. A row's
`available` flag is a live probe, so an uninstalled host renders as needing
configuration instead of failing when a session opens.

Kiro CLI is reached through its own ACP agent (`kiro-cli acp`) using the same
ACP stdio adapter as owner-registered endpoints. Override the executable when
it is not on `PATH` under the documented name:

```bash
loopx dashboard --kiro-cli-bin /path/to/kiro-cli
```

The trust boundary is unchanged: LoopX Chat answers every ACP
`session/request_permission` with `cancelled` and exposes no client host tools,
so a Kiro tool call that needs approval is refused rather than auto-approved.
LoopX passes no `--trust-all-tools`. Reaching the host this way drives one
read-only Chat session; it is not the governed `/goal` loop, which is entered
from a Kiro CLI session through the
[Kiro CLI goal-mode adapter](../../../loopx/kiro_cli_goal_mode/README.md).

Source-checkout development is a separate mode:

```bash
Expand Down
19 changes: 10 additions & 9 deletions apps/presentation/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,38 @@
"type": "module",
"scripts": {
"build": "tsc --noEmit && vite build && vite build --config vite.chat.config.ts",
"build:desktop": "tsc --noEmit && vite build",
"build:chat": "tsc --noEmit && vite build --config vite.chat.config.ts",
"build:desktop": "tsc --noEmit && vite build",
"dev": "bash ../../../scripts/dashboard-dev.sh",
"dev:web": "vite --host 127.0.0.1",
"export:frontstage-share": "node ../../../examples/export-frontstage-share-bundle.mjs",
"preview": "vite preview --host 127.0.0.1",
"smoke:action-packet": "rm -rf /tmp/loopx-action-packet-smoke && tsc --ignoreConfig --target ES2022 --module NodeNext --moduleResolution NodeNext --skipLibCheck --strict --outDir /tmp/loopx-action-packet-smoke smoke/action-packet-smoke.ts src/data/action-packet.ts && node /tmp/loopx-action-packet-smoke/smoke/action-packet-smoke.js",
"smoke:pwa-bundle": "python3 ../../../examples/dashboard-pwa-bundle-smoke.py",
"smoke:agent-family": "tsc --ignoreConfig --target ES2022 --module ES2022 --moduleResolution Bundler --skipLibCheck --strict --outDir node_modules/.cache/loopx-agent-family src/features/personal-workspace/agent-family.ts && node src/features/personal-workspace/agent-family.test.mjs",
"smoke:benchmark-study": "rm -rf node_modules/.cache/loopx-benchmark-study-smoke && tsc --ignoreConfig --target ES2022 --module NodeNext --moduleResolution NodeNext --skipLibCheck --strict --resolveJsonModule --outDir node_modules/.cache/loopx-benchmark-study-smoke smoke/benchmark-study-contract-smoke.ts src/data/benchmark-study.ts && node node_modules/.cache/loopx-benchmark-study-smoke/smoke/benchmark-study-contract-smoke.js",
"smoke:benchmark-study-browser": "node ../../../examples/dashboard-benchmark-study-browser-smoke.mjs",
"smoke:capability-configuration": "rm -rf node_modules/.cache/loopx-capability-configuration-smoke && tsc --ignoreConfig --target ES2022 --module NodeNext --moduleResolution NodeNext --types node --skipLibCheck --strict --outDir node_modules/.cache/loopx-capability-configuration-smoke smoke/capability-configuration-smoke.ts src/data/capability-configuration.ts && node node_modules/.cache/loopx-capability-configuration-smoke/smoke/capability-configuration-smoke.js",
"smoke:chat-route": "tsc --ignoreConfig --target ES2022 --module ES2022 --moduleResolution Bundler --ignoreDeprecations 6.0 --skipLibCheck --strict --outDir node_modules/.cache/loopx-chat-route-smoke smoke/chat-route-smoke.ts src/data/chat-model.ts src/vite-env.d.ts && node node_modules/.cache/loopx-chat-route-smoke/smoke/chat-route-smoke.js",
"smoke:demo-readiness": "bash ../../../scripts/loopx-python.sh --exec ../../../examples/dashboard-demo-readiness-smoke.py",
"smoke:frontstage-browser": "node ../../../examples/dashboard-frontstage-browser-smoke.mjs",
"smoke:frontstage-design-baseline": "node ../../../examples/dashboard-frontstage-design-baseline-smoke.mjs",
"smoke:frontstage-route": "rm -rf /tmp/loopx-frontstage-route-smoke && tsc --ignoreConfig --target ES2022 --module NodeNext --moduleResolution NodeNext --skipLibCheck --strict --outDir /tmp/loopx-frontstage-route-smoke smoke/frontstage-route-smoke.ts && node /tmp/loopx-frontstage-route-smoke/frontstage-route-smoke.js",
"smoke:frontstage-operator-state": "rm -rf /tmp/loopx-frontstage-operator-state-smoke && tsc --ignoreConfig --target ES2022 --module commonjs --moduleResolution node --ignoreDeprecations 6.0 --skipLibCheck --strict --outDir /tmp/loopx-frontstage-operator-state-smoke smoke/frontstage-operator-state-smoke.ts src/data/goal-channel-frontstage.ts && NODE_PATH=\"$PWD/node_modules\" node $(find /tmp/loopx-frontstage-operator-state-smoke -name 'frontstage-operator-state-smoke.js')",
"smoke:frontstage-route": "rm -rf /tmp/loopx-frontstage-route-smoke && tsc --ignoreConfig --target ES2022 --module NodeNext --moduleResolution NodeNext --skipLibCheck --strict --outDir /tmp/loopx-frontstage-route-smoke smoke/frontstage-route-smoke.ts && node /tmp/loopx-frontstage-route-smoke/frontstage-route-smoke.js",
"smoke:frontstage-share-bundle": "node ../../../examples/frontstage-share-bundle-smoke.mjs",
"smoke:home-browser": "node ../../../examples/dashboard-home-browser-smoke.mjs",
"smoke:goal-order": "tsc --ignoreConfig --target ES2022 --module ES2022 --moduleResolution Bundler --skipLibCheck --strict --outDir node_modules/.cache/loopx-goal-order src/features/personal-workspace/goal-order.ts && node src/features/personal-workspace/goal-order.test.mjs",
"smoke:home-browser": "node ../../../examples/dashboard-home-browser-smoke.mjs",
"smoke:home-route": "rm -rf /tmp/loopx-home-route-smoke && tsc --ignoreConfig --target ES2022 --module CommonJS --moduleResolution Node --ignoreDeprecations 6.0 --skipLibCheck --strict --outDir /tmp/loopx-home-route-smoke smoke/home-route-smoke.ts && node /tmp/loopx-home-route-smoke/home-route-smoke.js",
"smoke:personal-workspace": "npm run smoke:goal-order && node src/features/personal-workspace/workspace-theme.test.mjs && node ../../../examples/personal-workspace-browser-smoke.mjs",
"smoke:personal-workspace-packaged": "npm run smoke:goal-order && node src/features/personal-workspace/workspace-theme.test.mjs && LOOPX_PERSONAL_WORKSPACE_PACKAGED=1 LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node ../../../examples/personal-workspace-browser-smoke.mjs",
"smoke:personal-workspace-router": "rm -rf /tmp/loopx-personal-workspace-router-smoke && tsc --ignoreConfig --target ES2022 --module commonjs --moduleResolution node --ignoreDeprecations 6.0 --skipLibCheck --strict --outDir /tmp/loopx-personal-workspace-router-smoke smoke/personal-workspace-router-smoke.ts src/features/personal-workspace/personal-workspace-router.ts && node /tmp/loopx-personal-workspace-router-smoke/smoke/personal-workspace-router-smoke.js",
"smoke:home-route": "rm -rf /tmp/loopx-home-route-smoke && tsc --ignoreConfig --target ES2022 --module CommonJS --moduleResolution Node --ignoreDeprecations 6.0 --skipLibCheck --strict --outDir /tmp/loopx-home-route-smoke smoke/home-route-smoke.ts && node /tmp/loopx-home-route-smoke/home-route-smoke.js",
"smoke:presentation-surface-schema": "rm -rf /tmp/loopx-presentation-surface-schema-smoke && tsc --ignoreConfig --target ES2022 --module CommonJS --moduleResolution Node --ignoreDeprecations 6.0 --skipLibCheck --strict --resolveJsonModule --esModuleInterop --outDir /tmp/loopx-presentation-surface-schema-smoke smoke/presentation-surface-schema-smoke.ts src/data/status.ts src/data/decision-research.ts src/data/goal-channel-frontstage.ts && NODE_PATH=\"$PWD/node_modules\" node /tmp/loopx-presentation-surface-schema-smoke/apps/presentation/dashboard/smoke/presentation-surface-schema-smoke.js",
"smoke:projection-localization": "rm -rf /tmp/loopx-projection-localization-smoke && tsc --ignoreConfig --target ES2022 --module NodeNext --moduleResolution NodeNext --skipLibCheck --strict --outDir /tmp/loopx-projection-localization-smoke smoke/projection-localization-smoke.ts src/features/personal-workspace/projection-localization.ts && node /tmp/loopx-projection-localization-smoke/smoke/projection-localization-smoke.js",
"smoke:pwa-bundle": "python3 ../../../examples/dashboard-pwa-bundle-smoke.py",
"smoke:status-projection-contract": "rm -rf /tmp/loopx-status-projection-contract-smoke && tsc --ignoreConfig --target ES2022 --module CommonJS --moduleResolution Node --ignoreDeprecations 6.0 --skipLibCheck --strict --resolveJsonModule --esModuleInterop --outDir /tmp/loopx-status-projection-contract-smoke smoke/status-projection-contract-smoke.ts src/data/status.ts src/data/status-merge.ts src/data/status-request-fence.ts && NODE_PATH=\"$PWD/node_modules\" node /tmp/loopx-status-projection-contract-smoke/apps/presentation/dashboard/smoke/status-projection-contract-smoke.js",
"smoke:status-source-switch-browser": "node ../../../examples/status-source-switch-browser-smoke.mjs",
"smoke:status-source-switch-packaged": "LOOPX_STATUS_SOURCE_SWITCH_PACKAGED=1 LOOPX_STATUS_SOURCE_SWITCH_PORT=5198 node ../../../examples/status-source-switch-browser-smoke.mjs",
"smoke:status-sources": "rm -rf /tmp/loopx-status-source-smoke && tsc --ignoreConfig --target ES2022 --module CommonJS --moduleResolution Node --ignoreDeprecations 6.0 --skipLibCheck --strict --resolveJsonModule --esModuleInterop --outDir /tmp/loopx-status-source-smoke smoke/status-source-catalog-smoke.ts src/data/ssh-host-catalog.ts src/data/status-source-catalog.ts src/data/local-status-query.ts src/data/status.ts && NODE_PATH=\"$PWD/node_modules\" node /tmp/loopx-status-source-smoke/apps/presentation/dashboard/smoke/status-source-catalog-smoke.js",
"smoke:status-projection-contract": "rm -rf /tmp/loopx-status-projection-contract-smoke && tsc --ignoreConfig --target ES2022 --module CommonJS --moduleResolution Node --ignoreDeprecations 6.0 --skipLibCheck --strict --resolveJsonModule --esModuleInterop --outDir /tmp/loopx-status-projection-contract-smoke smoke/status-projection-contract-smoke.ts src/data/status.ts src/data/status-merge.ts src/data/status-request-fence.ts && NODE_PATH=\"$PWD/node_modules\" node /tmp/loopx-status-projection-contract-smoke/apps/presentation/dashboard/smoke/status-projection-contract-smoke.js",
"smoke:task-board-scroll": "node smoke/task-board-scroll-smoke.mjs",
"smoke:benchmark-study": "rm -rf node_modules/.cache/loopx-benchmark-study-smoke && tsc --ignoreConfig --target ES2022 --module NodeNext --moduleResolution NodeNext --skipLibCheck --strict --resolveJsonModule --outDir node_modules/.cache/loopx-benchmark-study-smoke smoke/benchmark-study-contract-smoke.ts src/data/benchmark-study.ts && node node_modules/.cache/loopx-benchmark-study-smoke/smoke/benchmark-study-contract-smoke.js",
"smoke:benchmark-study-browser": "node ../../../examples/dashboard-benchmark-study-browser-smoke.mjs",
"smoke:capability-configuration": "rm -rf node_modules/.cache/loopx-capability-configuration-smoke && tsc --ignoreConfig --target ES2022 --module NodeNext --moduleResolution NodeNext --types node --skipLibCheck --strict --outDir node_modules/.cache/loopx-capability-configuration-smoke smoke/capability-configuration-smoke.ts src/data/capability-configuration.ts && node node_modules/.cache/loopx-capability-configuration-smoke/smoke/capability-configuration-smoke.js",
"smoke:usage-progress": "rm -rf /tmp/loopx-usage-progress-smoke && tsc --ignoreConfig --target ES2022 --module CommonJS --moduleResolution Node --ignoreDeprecations 6.0 --resolveJsonModule --esModuleInterop --skipLibCheck --strict --outDir /tmp/loopx-usage-progress-smoke smoke/usage-progress-smoke.ts src/features/personal-workspace/personal-workspace-model.ts && node /tmp/loopx-usage-progress-smoke/apps/presentation/dashboard/smoke/usage-progress-smoke.js"
},
"dependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import assert from "node:assert/strict";
import {
agentFamily,
presentedAgentFamily,
} from "../../../node_modules/.cache/loopx-agent-family/agent-family.js";

// Positive: every documented shape still resolves to its host family, so a
// built-in Endpoint keeps its label.
assert.equal(agentFamily("kiro"), "kiro");
assert.equal(agentFamily("kiro-cli"), "kiro");
assert.equal(agentFamily("kiro_cli"), "kiro");
assert.equal(agentFamily("Kiro-Worker-1"), "kiro");
assert.equal(agentFamily("codex"), "codex");
assert.equal(agentFamily("codex-main-control"), "codex");
assert.equal(agentFamily("claude-code"), "claude");

// Negative: an unrelated operator id that merely begins with a family root
// keeps its own identity, matching the backend. Displaying it as the host would
// attribute another agent's work to Kiro/Codex/Claude.
assert.equal(agentFamily("kiroscope-worker"), "kiroscope-worker");
assert.equal(agentFamily("kirograph"), "kirograph");
assert.equal(agentFamily("codexplorer"), "codexplorer");
assert.equal(agentFamily("claudeflow"), "claudeflow");

// The typed adapter kind wins over the operator-chosen id when present, and the
// status projection placeholder is not treated as a family signal.
assert.equal(presentedAgentFamily("kiroscope-worker", "kiro-cli"), "kiro");
assert.equal(presentedAgentFamily("kiroscope-worker", null), "kiroscope-worker");
assert.equal(presentedAgentFamily("kiroscope-worker", ""), "kiroscope-worker");
assert.equal(
presentedAgentFamily("kiroscope-worker", "status_projection"),
"kiroscope-worker",
);

console.log("Agent family presentation invariants passed");
Loading