@skaile/workspaces 2.3.0 → 2.5.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.
- package/CHANGELOG.md +60 -0
- package/README.md +32 -13
- package/dist/app-sdk/index.js +1 -1
- package/dist/{asset-feeds-DKNYRLED.js → asset-feeds-4AWAT5PW.js} +6 -6
- package/dist/{asset-feeds-DKNYRLED.js.map → asset-feeds-4AWAT5PW.js.map} +1 -1
- package/dist/asset-manager/catalog-deployer.js +1 -1
- package/dist/asset-manager/index.js +5 -5
- package/dist/asset-manager/installer.js +2 -2
- package/dist/bridge/drivers/claude-sdk.js +108 -5
- package/dist/bridge/drivers/claude-sdk.js.map +1 -1
- package/dist/bridge/drivers/codex.js +1 -1
- package/dist/bridge/drivers/continue.js +2 -2
- package/dist/bridge/drivers/echo.js +1 -1
- package/dist/bridge/drivers/gemini.js +3 -3
- package/dist/bridge/drivers/goose.js +2 -2
- package/dist/bridge/drivers/omp.js +1 -1
- package/dist/bridge/drivers/opencode.js +2 -2
- package/dist/bridge/drivers/qwen.js +3 -3
- package/dist/bridge/index.js +2 -2
- package/dist/bridge/src/drivers/claude-sdk.d.ts +33 -0
- package/dist/bridge/src/drivers/claude-sdk.d.ts.map +1 -1
- package/dist/{chunk-CERGXDGG.js → chunk-2OCYS5EP.js} +3 -3
- package/dist/{chunk-CERGXDGG.js.map → chunk-2OCYS5EP.js.map} +1 -1
- package/dist/{chunk-W3Q37HEF.js → chunk-3HVASHBF.js} +3 -3
- package/dist/{chunk-W3Q37HEF.js.map → chunk-3HVASHBF.js.map} +1 -1
- package/dist/{chunk-UQ3RJKDD.js → chunk-4O2RAXKJ.js} +179 -52
- package/dist/chunk-4O2RAXKJ.js.map +1 -0
- package/dist/{chunk-DZM33LQO.js → chunk-4QROZZ5Z.js} +15 -3
- package/dist/chunk-4QROZZ5Z.js.map +1 -0
- package/dist/{chunk-G2ILW47R.js → chunk-57RFYEVM.js} +3 -3
- package/dist/{chunk-G2ILW47R.js.map → chunk-57RFYEVM.js.map} +1 -1
- package/dist/{chunk-BLWNI5KW.js → chunk-AKVAZ5CB.js} +2 -2
- package/dist/{chunk-BLWNI5KW.js.map → chunk-AKVAZ5CB.js.map} +1 -1
- package/dist/{chunk-BFIK6NPZ.js → chunk-AM5LFRXJ.js} +3 -3
- package/dist/{chunk-BFIK6NPZ.js.map → chunk-AM5LFRXJ.js.map} +1 -1
- package/dist/{chunk-RDE43ORT.js → chunk-D4VHBAJC.js} +4 -4
- package/dist/{chunk-RDE43ORT.js.map → chunk-D4VHBAJC.js.map} +1 -1
- package/dist/{chunk-PTS3QN5P.js → chunk-EEHOMCRU.js} +1021 -115
- package/dist/chunk-EEHOMCRU.js.map +1 -0
- package/dist/{chunk-CXX3UGJP.js → chunk-ENBTXMPL.js} +7 -7
- package/dist/{chunk-CXX3UGJP.js.map → chunk-ENBTXMPL.js.map} +1 -1
- package/dist/{chunk-LD6YGIKZ.js → chunk-EUWN4EK4.js} +60 -21
- package/dist/chunk-EUWN4EK4.js.map +1 -0
- package/dist/{chunk-FB4GPWYC.js → chunk-FKBQRV4J.js} +57 -16
- package/dist/chunk-FKBQRV4J.js.map +1 -0
- package/dist/{chunk-WG3L5VHF.js → chunk-HY3MBSNC.js} +2 -2
- package/dist/{chunk-WG3L5VHF.js.map → chunk-HY3MBSNC.js.map} +1 -1
- package/dist/{chunk-7IRH7MGV.js → chunk-IHX2SN35.js} +9 -9
- package/dist/{chunk-7IRH7MGV.js.map → chunk-IHX2SN35.js.map} +1 -1
- package/dist/{chunk-3F54MH2T.js → chunk-JAFEMG7W.js} +4 -4
- package/dist/{chunk-3F54MH2T.js.map → chunk-JAFEMG7W.js.map} +1 -1
- package/dist/{chunk-X6BPVUV2.js → chunk-L3TS7ZO4.js} +3 -3
- package/dist/{chunk-X6BPVUV2.js.map → chunk-L3TS7ZO4.js.map} +1 -1
- package/dist/{chunk-RIEXN7WA.js → chunk-MADO7PH5.js} +2 -2
- package/dist/{chunk-RIEXN7WA.js.map → chunk-MADO7PH5.js.map} +1 -1
- package/dist/{chunk-BCTU4TN7.js → chunk-MWFYXUFK.js} +3 -3
- package/dist/{chunk-BCTU4TN7.js.map → chunk-MWFYXUFK.js.map} +1 -1
- package/dist/{chunk-TK66CMXV.js → chunk-NU4E7C5I.js} +7 -3
- package/dist/chunk-NU4E7C5I.js.map +1 -0
- package/dist/{chunk-ZE2KEPE2.js → chunk-OEGHKO5N.js} +258 -24
- package/dist/chunk-OEGHKO5N.js.map +1 -0
- package/dist/{chunk-63ZPKREA.js → chunk-P43HBWKK.js} +11 -4
- package/dist/chunk-P43HBWKK.js.map +1 -0
- package/dist/{chunk-7UZGKVSK.js → chunk-PJ6INYDD.js} +3 -3
- package/dist/{chunk-7UZGKVSK.js.map → chunk-PJ6INYDD.js.map} +1 -1
- package/dist/{chunk-CA5CWLGN.js → chunk-PKEAYYEJ.js} +7 -4
- package/dist/chunk-PKEAYYEJ.js.map +1 -0
- package/dist/{chunk-ETFETLLB.js → chunk-UZYTSIX6.js} +6 -6
- package/dist/{chunk-ETFETLLB.js.map → chunk-UZYTSIX6.js.map} +1 -1
- package/dist/{chunk-SIF5F5SN.js → chunk-V4ZLZJFN.js} +3 -3
- package/dist/{chunk-SIF5F5SN.js.map → chunk-V4ZLZJFN.js.map} +1 -1
- package/dist/{chunk-ZR5Q4KZK.js → chunk-VNIKF66R.js} +3 -3
- package/dist/{chunk-ZR5Q4KZK.js.map → chunk-VNIKF66R.js.map} +1 -1
- package/dist/{chunk-CILACPUC.js → chunk-WSARNOPP.js} +5 -5
- package/dist/{chunk-CILACPUC.js.map → chunk-WSARNOPP.js.map} +1 -1
- package/dist/{chunk-TAJBFYCT.js → chunk-YWPEDILM.js} +83 -36
- package/dist/chunk-YWPEDILM.js.map +1 -0
- package/dist/{chunk-UNP4M5ZZ.js → chunk-ZR2X4SIG.js} +4 -4
- package/dist/{chunk-UNP4M5ZZ.js.map → chunk-ZR2X4SIG.js.map} +1 -1
- package/dist/cli/index.js +50 -50
- package/dist/connectors/config.js +1 -1
- package/dist/connectors/index.js +6 -6
- package/dist/core/index.js +1 -1
- package/dist/core/src/subprocess.d.ts +5 -1
- package/dist/core/src/subprocess.d.ts.map +1 -1
- package/dist/deploy/index.js +1 -1
- package/dist/discovery/index.js +1 -1
- package/dist/{ensure-sources-R6KW7K3M.js → ensure-sources-QF72HVUM.js} +8 -8
- package/dist/{ensure-sources-R6KW7K3M.js.map → ensure-sources-QF72HVUM.js.map} +1 -1
- package/dist/factory-assets/connectors/box.js +6 -6
- package/dist/factory-assets/connectors/deploy.js +6 -6
- package/dist/factory-assets/connectors/devserver.js +6 -6
- package/dist/factory-assets/connectors/flow/CONNECTOR.md +61 -14
- package/dist/factory-assets/connectors/flow/adapter.d.ts +78 -34
- package/dist/factory-assets/connectors/flow/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/adapter.js +6 -6
- package/dist/factory-assets/connectors/flow/contract/expression.d.ts +55 -0
- package/dist/factory-assets/connectors/flow/contract/expression.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract/flow.v2.schema.json +61 -134
- package/dist/factory-assets/connectors/flow/contract/index.d.ts +1 -1
- package/dist/factory-assets/connectors/flow/contract/index.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract/normalize.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract/schema.d.ts +14 -2
- package/dist/factory-assets/connectors/flow/contract/schema.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract.js +2 -2
- package/dist/factory-assets/connectors/flow/engine/bindings.d.ts +9 -0
- package/dist/factory-assets/connectors/flow/engine/bindings.d.ts.map +1 -0
- package/dist/factory-assets/connectors/flow/engine/engine.d.ts +11 -6
- package/dist/factory-assets/connectors/flow/engine/engine.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/flow-kind-provider.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/index.d.ts +2 -0
- package/dist/factory-assets/connectors/flow/engine/index.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/node-shape.d.ts +22 -0
- package/dist/factory-assets/connectors/flow/engine/node-shape.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/output-schema.d.ts +18 -0
- package/dist/factory-assets/connectors/flow/engine/output-schema.d.ts.map +1 -0
- package/dist/factory-assets/connectors/flow/engine/types.d.ts +21 -15
- package/dist/factory-assets/connectors/flow/engine/types.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine.js +4 -4
- package/dist/factory-assets/connectors/flow/index.d.ts +3 -0
- package/dist/factory-assets/connectors/flow/index.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/inline-sub-flow-host.d.ts +48 -0
- package/dist/factory-assets/connectors/flow/inline-sub-flow-host.d.ts.map +1 -0
- package/dist/factory-assets/connectors/flow/node-executor.d.ts +45 -0
- package/dist/factory-assets/connectors/flow/node-executor.d.ts.map +1 -0
- package/dist/factory-assets/connectors/flow/prompt-fragments.d.ts +3 -2
- package/dist/factory-assets/connectors/flow/prompt-fragments.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/prompt-fragments.js +2 -2
- package/dist/factory-assets/connectors/flow/run-flow.d.ts +10 -9
- package/dist/factory-assets/connectors/flow/run-flow.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/run-flow.js +7 -7
- package/dist/factory-assets/connectors/flow/sub-flow-runner.d.ts +73 -0
- package/dist/factory-assets/connectors/flow/sub-flow-runner.d.ts.map +1 -0
- package/dist/factory-assets/connectors/flow.js +6 -6
- package/dist/factory-assets/connectors/git.js +6 -6
- package/dist/factory-assets/connectors/gmail.js +6 -6
- package/dist/factory-assets/connectors/googledrive.js +6 -6
- package/dist/factory-assets/connectors/local.js +6 -6
- package/dist/factory-assets/connectors/mattermost.js +6 -6
- package/dist/factory-assets/connectors/memory.js +6 -6
- package/dist/factory-assets/connectors/minio.js +6 -6
- package/dist/factory-assets/connectors/postgres.js +6 -6
- package/dist/factory-assets/connectors/s3.js +6 -6
- package/dist/factory-assets/connectors/sharepoint.js +6 -6
- package/dist/factory-assets/connectors/sqlite.js +6 -6
- package/dist/factory-assets/connectors/static-server.js +6 -6
- package/dist/factory-assets/connectors/tunnel.js +6 -6
- package/dist/factory-assets/connectors/webdav.js +6 -6
- package/dist/factory-assets/connectors/xstate-store.js +6 -6
- package/dist/factory-assets/connectors/xstate.js +6 -6
- package/dist/{flows-F3CJNMK6.js → flows-YIW4HL3L.js} +4 -4
- package/dist/{flows-F3CJNMK6.js.map → flows-YIW4HL3L.js.map} +1 -1
- package/dist/{helpers-KPOLCGKX.js → helpers-LURCZHA4.js} +8 -8
- package/dist/{helpers-KPOLCGKX.js.map → helpers-LURCZHA4.js.map} +1 -1
- package/dist/library/index.js +3 -3
- package/dist/library/install.js +1 -1
- package/dist/{library-publish-ZAPZS64O.js → library-publish-2GMJM3NZ.js} +14 -14
- package/dist/{library-publish-ZAPZS64O.js.map → library-publish-2GMJM3NZ.js.map} +1 -1
- package/dist/{open-library-I4F2UNOY.js → open-library-POFOXZWH.js} +12 -12
- package/dist/{open-library-I4F2UNOY.js.map → open-library-POFOXZWH.js.map} +1 -1
- package/dist/{paths-KBACHM5B.js → paths-ZLX4ITAQ.js} +6 -6
- package/dist/{paths-KBACHM5B.js.map → paths-ZLX4ITAQ.js.map} +1 -1
- package/dist/{plugin-store-CYSNVZIX.js → plugin-store-YIMNH2FH.js} +3 -3
- package/dist/{plugin-store-CYSNVZIX.js.map → plugin-store-YIMNH2FH.js.map} +1 -1
- package/dist/{project-SIHZBTGB.js → project-GVOPZEJZ.js} +9 -9
- package/dist/{project-SIHZBTGB.js.map → project-GVOPZEJZ.js.map} +1 -1
- package/dist/resolver/index.js +1 -1
- package/dist/runner/index.js +15 -12
- package/dist/runner/src/serve.d.ts +3 -1
- package/dist/runner/src/serve.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +5 -5
- package/dist/sdk/bridge.js +2 -2
- package/dist/sdk/core.js +1 -1
- package/dist/sdk/flow.js +4 -4
- package/dist/sdk/index.js +15 -12
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/resolver.js +1 -1
- package/dist/sdk/runner.js +15 -12
- package/dist/sdk/session.js +2 -2
- package/dist/sdk/types.js +1 -1
- package/dist/session/index.js +2 -2
- package/dist/session/src/dispatcher.d.ts.map +1 -1
- package/dist/{setup-KMBXMV25.js → setup-RHWARBBB.js} +5 -5
- package/dist/{setup-KMBXMV25.js.map → setup-RHWARBBB.js.map} +1 -1
- package/dist/{skills-LHS4TOKK.js → skills-HGVH6UTA.js} +5 -5
- package/dist/{skills-LHS4TOKK.js.map → skills-HGVH6UTA.js.map} +1 -1
- package/dist/{source-X226ZJPR.js → source-FB3T4IHP.js} +13 -13
- package/dist/{source-X226ZJPR.js.map → source-FB3T4IHP.js.map} +1 -1
- package/dist/{source-manifest-publish-N2QVUR5U.js → source-manifest-publish-XTRDVPSC.js} +15 -15
- package/dist/{source-manifest-publish-N2QVUR5U.js.map → source-manifest-publish-XTRDVPSC.js.map} +1 -1
- package/dist/tui/index.js +15 -12
- package/dist/tui/index.js.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/src/events.d.ts +20 -1
- package/dist/types/src/events.d.ts.map +1 -1
- package/dist/types/src/flow.d.ts +26 -0
- package/dist/types/src/flow.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/protocol.d.ts +1 -0
- package/dist/types/src/protocol.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +12 -1
- package/dist/types/src/version.d.ts.map +1 -1
- package/dist/workspace-plugin/adapters/mcp.js +4 -4
- package/dist/workspace-plugin/adapters/omp.js +7 -7
- package/dist/workspace-plugin/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-63ZPKREA.js.map +0 -1
- package/dist/chunk-CA5CWLGN.js.map +0 -1
- package/dist/chunk-DZM33LQO.js.map +0 -1
- package/dist/chunk-FB4GPWYC.js.map +0 -1
- package/dist/chunk-LD6YGIKZ.js.map +0 -1
- package/dist/chunk-PTS3QN5P.js.map +0 -1
- package/dist/chunk-TAJBFYCT.js.map +0 -1
- package/dist/chunk-TK66CMXV.js.map +0 -1
- package/dist/chunk-UQ3RJKDD.js.map +0 -1
- package/dist/chunk-ZE2KEPE2.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,65 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#500](https://github.com/skaile-ai/workspaces/pull/500) [`5104b98`](https://github.com/skaile-ai/workspaces/commit/5104b980e3e7243f19142079257c5fc42be20722) Thanks [@peteralbert](https://github.com/peteralbert)! - Protocol 3.8 — acknowledge receipt of a prompt
|
|
8
|
+
|
|
9
|
+
`prompt` was the only frame in the protocol with no acknowledgement. A host sent
|
|
10
|
+
it into the socket and got nothing back, so a runner that had stopped executing
|
|
11
|
+
while its WebSocket stayed open (an in-container OOM is enough) swallowed every
|
|
12
|
+
subsequent user message with no trace anywhere: no log, no event, no failed
|
|
13
|
+
send. The host had no way to distinguish "delivered, still thinking" from "never
|
|
14
|
+
arrived".
|
|
15
|
+
|
|
16
|
+
The `prompt` command gains an optional `messageId`. When it is set the runner
|
|
17
|
+
emits exactly one new `prompt_ack` event echoing it back, as the **first act** of
|
|
18
|
+
`handlePrompt` — before `awaitSessionReadyOrFail()` (which drops prompts
|
|
19
|
+
silently), before the secrets check, and before the driver starts. When it is
|
|
20
|
+
absent nothing is emitted, so a 3.7 host sees byte-identical behaviour, and a
|
|
21
|
+
3.8 host talking to a 3.7 runner must feature-detect on
|
|
22
|
+
`major === 3 && minor >= 8` rather than read the missing ack as a lost prompt.
|
|
23
|
+
|
|
24
|
+
The ack is deliberately receipt-only: it says the frame arrived and was decoded,
|
|
25
|
+
never that the turn ran. A driver that accepts a prompt and then leaves the turn
|
|
26
|
+
promise pending is a separate failure mode and needs turn-level supervision.
|
|
27
|
+
|
|
28
|
+
`prompt_ack` is transient — system RPC, not chat. It is on the dispatcher's
|
|
29
|
+
denylist so it is never persisted (an unlisted ack would also split the
|
|
30
|
+
assistant's streaming bubble mid-turn).
|
|
31
|
+
|
|
32
|
+
### Patch Changes
|
|
33
|
+
|
|
34
|
+
- [#501](https://github.com/skaile-ai/workspaces/pull/501) [`1076360`](https://github.com/skaile-ai/workspaces/commit/107636009ebbbfeb7d82b93a2a8633f84dfc59b6) Thanks [@peteralbert](https://github.com/peteralbert)! - Fix `ClaudeSdkDriver` so aborting a turn (Stop) actually kills an in-flight
|
|
35
|
+
tool subprocess (e.g. a running Bash command) instead of leaving it orphaned.
|
|
36
|
+
`query.close()` only tore down the SDK's connection to the CLI subprocess, not
|
|
37
|
+
whatever process that CLI in turn spawned for the tool call, and a
|
|
38
|
+
process-group signal to the CLI didn't reach it either since the CLI spawns
|
|
39
|
+
the tool's shell into its own new process group. The driver now captures the
|
|
40
|
+
CLI subprocess's own OS pid via `spawnClaudeCodeProcess`, snapshots its
|
|
41
|
+
descendant processes before sending any abort signal (reparenting on exit
|
|
42
|
+
would otherwise make a post-abort snapshot come back empty), and SIGKILLs
|
|
43
|
+
that snapshot from `abort()` and `kill()` alike.
|
|
44
|
+
|
|
45
|
+
## 2.4.0
|
|
46
|
+
|
|
47
|
+
### Minor Changes
|
|
48
|
+
|
|
49
|
+
- [#497](https://github.com/skaile-ai/workspaces/pull/497) [`b282668`](https://github.com/skaile-ai/workspaces/commit/b2826683a48b4075e42e4a86704912cb9f9c8634) Thanks [@peteralbert](https://github.com/peteralbert)! - Add `sub-flow` as the seventh Flow v2 node kind and run delegated flows once,
|
|
50
|
+
inline, through the existing session agent, deterministic executor, gate, and
|
|
51
|
+
FlowAdapter lifecycles. Exact legacy sub-flow nodes now normalize into this
|
|
52
|
+
active shape, and dependency discovery emits duplicate-safe flow requirements.
|
|
53
|
+
Parked child interactions project onto the durable parent node while resumable
|
|
54
|
+
child adapters remain transient in memory; canonical identity, ancestry, and
|
|
55
|
+
depth guards bound delegation without adding child records or state fields.
|
|
56
|
+
|
|
57
|
+
- [#493](https://github.com/skaile-ai/workspaces/pull/493) [`6bcbbfd`](https://github.com/skaile-ai/workspaces/commit/6bcbbfd5697f5b2528944ff8954d11f28a8e9e48) Thanks [@peteralbert](https://github.com/peteralbert)! - Execute Flow v2 function nodes through the session workspace without starting a model turn, including non-success signal termination.
|
|
58
|
+
|
|
59
|
+
- [#496](https://github.com/skaile-ai/workspaces/pull/496) [`92da12c`](https://github.com/skaile-ai/workspaces/commit/92da12cd71ccf7b8e393880efa675ccf2c1f994d) Thanks [@peteralbert](https://github.com/peteralbert)! - Run strict Flow v2 gate nodes as mandatory runtime-owned human decisions that park without a model, complete on approval, and preserve rejection feedback for revision and re-request.
|
|
60
|
+
|
|
61
|
+
- [#495](https://github.com/skaile-ai/workspaces/pull/495) [`39c2da3`](https://github.com/skaile-ai/workspaces/commit/39c2da359591d6727f1b2a843d81dafb43912de0) Thanks [@peteralbert](https://github.com/peteralbert)! - Resolve Flow v2 node bindings with provenance, enforce preconditions, and validate downstream-consumed output fields with one durable agent correction.
|
|
62
|
+
|
|
3
63
|
## 2.3.0
|
|
4
64
|
|
|
5
65
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -11,8 +11,8 @@ published package. Every internal folder is exposed as a subpath export and the
|
|
|
11
11
|
| **core** | `@skaile/workspaces/core` | Shared types, manifest parsing, settings, store, framework config. Zero heavy deps. |
|
|
12
12
|
| **types** | `@skaile/workspaces/types` | Unified message types, event types, and runtime interfaces. The shared contract. |
|
|
13
13
|
| **bridge** | `@skaile/workspaces/bridge` | Pluggable agent driver abstraction. Backends: Claude SDK, Codex, OMP. |
|
|
14
|
-
| **flow engine** | `@skaile/workspaces/factory-assets/connectors/flow/engine` | Flow state machine with active strict v2 agent nodes and legacy skill compatibility. |
|
|
15
|
-
| **flow contract** | `@skaile/workspaces/factory-assets/connectors/flow/contract` | Strict Flow v2 authoring schemas, v1 normalization, bounded expressions, and canonical content identity. |
|
|
14
|
+
| **flow engine** | `@skaile/workspaces/factory-assets/connectors/flow/engine` | Flow state machine with typed bindings, precondition admission, consumed-output validation, active strict v2 agent/function/gate nodes, and legacy skill compatibility. |
|
|
15
|
+
| **flow contract** | `@skaile/workspaces/factory-assets/connectors/flow/contract` | Strict Flow v2 authoring schemas, v1 normalization, bounded expressions, binding origins, and canonical content identity. |
|
|
16
16
|
| **resolver** | `@skaile/workspaces/resolver` | Parses SKILL.md requirements (reads_from, writes_to), validates gates, generates agent preamble. |
|
|
17
17
|
| **client** | `@skaile/workspaces/client` | Framework-agnostic protocol client. Implements IAgentRuntime over a pluggable transport. |
|
|
18
18
|
| **transport** | `@skaile/workspaces/transport` | Transport adapters (WebSocket client/server). Pluggable — add stdio, IPC, etc. |
|
|
@@ -56,9 +56,9 @@ cli (top-level CLI — orchestrates everything below)
|
|
|
56
56
|
|
|
57
57
|
1. **User runs `skaile run <flow>`** → **cli** parses args, resolves the flow file, and delegates to **runner**.
|
|
58
58
|
|
|
59
|
-
2. **runner** loads the flow through the shared v2 parser, then the flow engine computes which agent nodes are available from completed/skipped state.
|
|
59
|
+
2. **runner** loads the flow through the shared v2 parser, then the flow engine computes which agent, function, and gate nodes are available from completed/skipped state, hard edges, and deterministic preconditions.
|
|
60
60
|
|
|
61
|
-
3.
|
|
61
|
+
3. Available function nodes run with resolved bindings in the existing session workspace without a model turn. Available gate nodes park for a human decision without an executor or model. For an agent node, the flow connector presents its interpolated instruction, resolved values and origins, reusable asset requirements, and predecessor context to the session agent.
|
|
62
62
|
|
|
63
63
|
4. **bridge** starts the LLM backend (Claude SDK or Codex in-process, or OMP as a subprocess) and streams `AgentEvent`s (defined in **types**) back to the runner.
|
|
64
64
|
|
|
@@ -84,20 +84,39 @@ import {
|
|
|
84
84
|
} from "@skaile/workspaces/factory-assets/connectors/flow/contract"
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
`check`, `gate`,
|
|
89
|
-
|
|
87
|
+
Flow Engine v2 defines seven node kinds: `agent`, `subprompt`, `function`,
|
|
88
|
+
`check`, `gate`, `router`, and inline `sub-flow`. It
|
|
89
|
+
rejects unknown v2 keys with precise paths, and
|
|
90
|
+
normalizes recognized v1 definitions deterministically. Exact legacy sub-flow
|
|
91
|
+
nodes now activate, while every other legacy kind retains its existing
|
|
92
|
+
compatibility state trace.
|
|
90
93
|
Structured output references are checked against declared producer fields, and
|
|
91
94
|
gates carry a strict response schema. The generated schema is published as both
|
|
92
95
|
`contract/flow.v2.schema.json` and `contract/schema.json`.
|
|
93
|
-
|
|
94
|
-
other legacy kind remains an inert
|
|
95
|
-
parameters, assets, and dependency
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
Explicit legacy `type: skill` and exact `type: sub-flow` nodes normalize to
|
|
97
|
+
their executable v2 kinds; every other legacy kind remains an inert
|
|
98
|
+
compatibility placeholder with its IDs, parameters, assets, and dependency
|
|
99
|
+
semantics retained.
|
|
100
|
+
The runtime activates `agent`, `function`, `gate`, and `sub-flow`. Interpolated agent instructions,
|
|
101
|
+
resolved values, and origins reach the session agent through `build_handoff(nodeId)`; function nodes
|
|
102
|
+
run shell, Node, or Python commands in the existing session workspace without a
|
|
103
|
+
model turn, with resolved values exported as environment variables. Gate nodes
|
|
104
|
+
park immediately through the durable approval state, complete on approval, and
|
|
105
|
+
preserve rejection feedback for revision and a later approval re-request.
|
|
106
|
+
Sub-flow nodes load one child inline through the same session, driver,
|
|
107
|
+
`NodeExecutor`, gate operations, and `FlowAdapter` lifecycle. Parked approval and
|
|
108
|
+
input interactions are published on the parent sub-flow node, while the child
|
|
109
|
+
adapter remains transient and resumable in memory. Canonical flow identities,
|
|
110
|
+
cycle detection, and a depth bound keep resolution contained. Sub-flows create
|
|
111
|
+
no run group, loop behavior, second executor, child record, or new durable-state
|
|
112
|
+
field.
|
|
113
|
+
Downstream-consumed output schemas are enforced at completion. Agents receive one
|
|
114
|
+
durable corrective call; deterministic functions fail directly. Unconsumed schemas
|
|
115
|
+
remain prose-only.
|
|
98
116
|
Skills, connectors, mounts, MCP servers, and knowledge stay reusable
|
|
99
117
|
`run.assets` requirements, and shared refs are deduplicated in the dependency
|
|
100
|
-
graph.
|
|
118
|
+
graph. This release activates `agent`, `function`, `gate`, and `sub-flow`; other current
|
|
119
|
+
kinds remain inert for later releases.
|
|
101
120
|
`FlowDefinition` is the strict authored-v2 type; parser results use the wider
|
|
102
121
|
`ParsedFlowDefinition` so unknown legacy edge kinds and their metadata can be
|
|
103
122
|
retained inertly without weakening authored validation.
|
package/dist/app-sdk/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { assignDomain, buildDomainIndex, DOMAIN_MD_FILENAME, parseDomainMd } from './chunk-
|
|
1
|
+
import { assignDomain, buildDomainIndex, DOMAIN_MD_FILENAME, parseDomainMd } from './chunk-NU4E7C5I.js';
|
|
2
2
|
import './chunk-JXT46QFC.js';
|
|
3
|
-
import './chunk-
|
|
3
|
+
import './chunk-P43HBWKK.js';
|
|
4
4
|
import './chunk-F3HNW354.js';
|
|
5
5
|
import './chunk-K5GBV4SA.js';
|
|
6
6
|
import './chunk-ZAP2BDHG.js';
|
|
@@ -65,7 +65,7 @@ async function libraryAssets() {
|
|
|
65
65
|
const domains = [];
|
|
66
66
|
try {
|
|
67
67
|
const [{ openLibraryManager }, { skaileHomeDir }, { getGlobalCacheDir }] = await Promise.all([
|
|
68
|
-
import('./open-library-
|
|
68
|
+
import('./open-library-POFOXZWH.js'),
|
|
69
69
|
import('./library/index.js'),
|
|
70
70
|
import('./core/index.js')
|
|
71
71
|
]);
|
|
@@ -98,7 +98,7 @@ async function libraryAssets() {
|
|
|
98
98
|
async function storeAssets(projectDir) {
|
|
99
99
|
const notes = [];
|
|
100
100
|
try {
|
|
101
|
-
const { resolveCatalogSource } = await import('./open-library-
|
|
101
|
+
const { resolveCatalogSource } = await import('./open-library-POFOXZWH.js');
|
|
102
102
|
const { source, close } = await resolveCatalogSource({ projectDir });
|
|
103
103
|
try {
|
|
104
104
|
const assets = await source.listAssets();
|
|
@@ -202,5 +202,5 @@ function dedupeBySlug(domains) {
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
export { gatherAssetFeeds };
|
|
205
|
-
//# sourceMappingURL=asset-feeds-
|
|
206
|
-
//# sourceMappingURL=asset-feeds-
|
|
205
|
+
//# sourceMappingURL=asset-feeds-4AWAT5PW.js.map
|
|
206
|
+
//# sourceMappingURL=asset-feeds-4AWAT5PW.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../cli/src/asset-feeds.ts"],"names":[],"mappings":"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","file":"asset-feeds-DKNYRLED.js","sourcesContent":["/**\n * asset-feeds.ts — gather installable assets from every feed the manage TUI\n * surfaces.\n *\n * Four feeds, normalized into `CatalogEntry`:\n * - **Project sources** — `skaile.yaml` `sources:` entries (scanned via\n * `AssetManager.search()`; the new `sources:` field is merged into the\n * legacy repositories map in `AssetManager.loadConfig`).\n * - **Local libraries** — user authoring places from `LocalIndex` (filesystem\n * scan of each library's `path`).\n * - **Factory assets** — the bundled first-party `factory-assets` tree\n * (`factoryAssetRoots()`), scanned from disk. Always present; no network.\n * - **Remote store** — the configured catalog source when `catalog.url` is\n * not `local`. Failures are non-fatal — they surface as notes.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport {\n type AssetKind,\n type CatalogDomain,\n type CatalogEntry,\n factoryAssetRoots,\n parseFrontmatter,\n scanRepo,\n walkDir,\n} from \"@skaile/workspaces/core\";\nimport {\n assignDomain,\n buildDomainIndex,\n DOMAIN_MD_FILENAME,\n type DomainMd,\n parseDomainMd,\n} from \"@skaile/workspaces/discovery\";\n\n/** One non-fatal note returned to the caller for surfacing in the UI. */\nexport interface FeedNote {\n feed: \"sources\" | \"libraries\" | \"store\" | \"factory\";\n message: string;\n}\n\n/**\n * Which feed an asset came from. This is the top-level grouping axis in the\n * manage TUI (\"where is this skill coming from?\") and is intentionally distinct\n * from `publisher` (asset-intrinsic identity): the same publisher can legitimately\n * appear under more than one origin (e.g. a github repo you added as a `source`\n * that is also listed in the remote `store`).\n */\nexport type AssetOrigin = \"source\" | \"library\" | \"factory\" | \"store\";\n\n/** A catalog entry tagged with the feed it was gathered from. */\nexport interface OriginEntry {\n origin: AssetOrigin;\n entry: CatalogEntry;\n /**\n * Second-level grouping label for the manage tree. Defaults (when unset) to the\n * entry's `publisher`; the source feed sets it to the source's short repo name\n * so the Sources band groups by source — \"which repo did this come from\" — not\n * by the asset-intrinsic publisher.\n */\n group?: string;\n}\n\nexport interface AssetFeedResult {\n entries: OriginEntry[];\n /**\n * Display-only domain metadata, keyed (by the consumer) on `slug`. Populated\n * from the store's `catalog.listDomains` when the connected catalog source\n * exposes it; empty otherwise. The manage TUI joins `entry.domainSlug` → a\n * domain for grouping headers, descriptions, and relation badges.\n */\n domains: CatalogDomain[];\n notes: FeedNote[];\n}\n\n// ── Domain stamping (on-disk feeds) ──────────────────────────────────────────\n//\n// `scanRepo` (the source / library / factory scan path) does not read the\n// `DOMAIN.md` sidecars, so its entries arrive with no `domainSlug` and render\n// flat under their publisher band. We re-derive the grouping here from the\n// root's `DOMAIN.md` files (the same sidecars the remote store computes\n// server-side), and fall back to the repo/source name for any entry no domain\n// claims so it still groups instead of landing in the synthetic \"no domain\" pile.\n\n/** Repo-relative POSIX path (DOMAIN.md slugs are matched whole-segment, `/`-joined). */\nfunction toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\n/** Parse every `DOMAIN.md` sidecar under `root` into a {@link DomainMd}. */\nasync function loadDomainMds(root: string): Promise<DomainMd[]> {\n const out: DomainMd[] = [];\n for await (const { path: filePath, name } of walkDir(root)) {\n if (name !== DOMAIN_MD_FILENAME) continue;\n try {\n const { data } = parseFrontmatter(readFileSync(filePath, \"utf8\"));\n out.push(parseDomainMd(toPosix(path.relative(root, filePath)), data));\n } catch {}\n }\n return out;\n}\n\n/**\n * Stamp `domainSlug` onto every entry scanned from `root` using the root's\n * `DOMAIN.md` sidecars. Returns the `CatalogDomain` registry for the real\n * domains, plus a synthetic fallback domain when any entry used the fallback.\n * Mutates `entries` in place (sets `entry.domainSlug`).\n *\n * `synthesizeFallback` (default true): entries no domain claims fall back to\n * `fallbackSlug` (the repo/source name). Pass `false` when the caller already\n * groups these entries one level up (e.g. the source feed groups by source\n * node) — unmatched entries then get no domain (`\"\"`) and hang directly under\n * that parent, instead of nesting under a same-named fallback domain.\n */\nasync function stampDomains(\n root: string,\n fallbackSlug: string,\n entries: CatalogEntry[],\n synthesizeFallback = true,\n): Promise<CatalogDomain[]> {\n const domainMds = await loadDomainMds(root);\n const assignables = entries.map((entry) => ({\n entry,\n ref: `${entry.kind}:${entry.name}`,\n relativePath: toPosix(path.relative(root, entry.source)),\n }));\n\n let usedFallback = false;\n for (const a of assignables) {\n const slug = assignDomain(a.relativePath, domainMds);\n if (slug) a.entry.domainSlug = slug;\n else if (synthesizeFallback) {\n a.entry.domainSlug = fallbackSlug;\n usedFallback = true;\n } else a.entry.domainSlug = \"\";\n }\n\n const { index } = buildDomainIndex(assignables, domainMds);\n const catalog: CatalogDomain[] = Object.values(index).map((d) => ({\n slug: d.slug,\n title: d.title,\n ...(d.description ? { description: d.description } : {}),\n ...(d.relation ? { relation: d.relation } : {}),\n ...(d.order !== undefined ? { order: d.order } : {}),\n ...(d.recommended[0] ? { recommendedRef: d.recommended[0] } : {}),\n }));\n if (usedFallback) catalog.push({ slug: fallbackSlug, title: fallbackSlug });\n return catalog;\n}\n\n/**\n * Pull assets from local libraries (the authoring places under\n * `~/.skaile/libraries/<name>/`, not the `~/.skaile/cache/sources/` cache). Each\n * library's `path` is scanned with `scanRepo` and grouped under the bare library\n * name (`group: <name>`) so the manage TUI shows a clean `<name>` band — no\n * `library:` prefix and no redundant same-named fallback domain (mirrors the\n * source feed).\n */\nasync function libraryAssets(): Promise<{\n entries: OriginEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n const domains: CatalogDomain[] = [];\n try {\n const [{ openLibraryManager }, { skaileHomeDir }, { getGlobalCacheDir }] = await Promise.all([\n import(\"./open-library.ts\"),\n import(\"@skaile/workspaces/library\"),\n import(\"@skaile/workspaces/core\"),\n ]);\n const { manager, close } = await openLibraryManager();\n try {\n // Authoring libraries are the rows that are NOT source clones. index.db\n // rows predating the cache migration still carry the legacy\n // ~/.skaile/sources path, so exclude both roots.\n const newRoot = getGlobalCacheDir();\n const legacyRoot = path.join(skaileHomeDir(), \"sources\");\n const libs = (await manager.listLibraries()).filter(\n (l) => !l.path.startsWith(newRoot) && !l.path.startsWith(legacyRoot),\n );\n const entries: OriginEntry[] = [];\n for (const lib of libs) {\n if (!existsSync(lib.path)) continue;\n // Scan + group under the bare library name. `synthesizeFallback: false`\n // (as the source feed does) keeps domainless assets directly under the\n // library band instead of a redundant same-named fallback domain.\n const libEntries = [...(await scanRepo(lib.path, lib.name))];\n domains.push(...(await stampDomains(lib.path, lib.name, libEntries, false)));\n for (const entry of libEntries) entries.push({ origin: \"library\", entry, group: lib.name });\n }\n return { entries, domains, notes };\n } finally {\n close();\n }\n } catch (err) {\n notes.push({\n feed: \"libraries\",\n message: err instanceof Error ? err.message : String(err),\n });\n return { entries: [], domains, notes };\n }\n}\n\n/**\n * Pull a (best-effort) snapshot of the connected remote store catalog.\n *\n * - Returns no entries when the resolved catalog is local-only or the host\n * is unreachable (the failure becomes a note, not an exception).\n * - Maps `CatalogAsset` → `CatalogEntry`, carrying `publisher`, `repo`, and\n * `category` straight through and using `source = \"store://<id>\"`; per-file\n * `source` paths are not available for remote assets so the canonical ref is\n * a stable placeholder.\n */\nasync function storeAssets(projectDir: string): Promise<{\n entries: CatalogEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n try {\n const { resolveCatalogSource } = await import(\"./open-library.ts\");\n const { source, close } = await resolveCatalogSource({ projectDir });\n try {\n const assets = await source.listAssets();\n const entries: CatalogEntry[] = assets.map((a) => ({\n name: a.name,\n kind: (a.kind as AssetKind) ?? \"skill\",\n description: a.description ?? \"\",\n source: `store://${a.id}`,\n publisher: a.publisher,\n repo: a.repo,\n domainSlug: a.domainSlug,\n category: a.category,\n version: a.version,\n requires: [],\n dependencies: [],\n }));\n // Domains are optional on the catalog source — only the store backend\n // (post Part 4 `catalog.listDomains`) exposes them. Duck-type the call so\n // older sources and the local catalog degrade to no domains, not an error.\n const domains = await loadDomains(source);\n return { entries, domains, notes };\n } finally {\n close();\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // Local-only catalog throws a recognizable message — silence it; absence\n // is the user's choice, not an error.\n if (!/local/i.test(msg)) {\n notes.push({ feed: \"store\", message: msg });\n }\n return { entries: [], domains: [], notes };\n }\n}\n\n/** Optional `listDomains` capability — returns `[]` when the source lacks it. */\nasync function loadDomains(source: unknown): Promise<CatalogDomain[]> {\n const fn = (source as { listDomains?: () => Promise<CatalogDomain[]> }).listDomains;\n if (typeof fn !== \"function\") return [];\n const domains = await fn.call(source);\n return Array.isArray(domains) ? domains : [];\n}\n\n/** The slice of `AssetManager` the source feed needs (kept narrow for tests). */\ninterface AssetManagerLike {\n search: () => Promise<CatalogEntry[]>;\n searchRepoRoots: () => { repoName: string; root: string }[];\n}\n\n/**\n * Project-source feed: leans on `AssetManager.search()`, which now merges\n * `skaile.yaml`'s `sources:` field into the repositories map, then stamps\n * `DOMAIN.md`-derived domain grouping onto the scanned entries via\n * `searchRepoRoots()`.\n */\nasync function sourceAssets(\n am: AssetManagerLike,\n): Promise<{ entries: OriginEntry[]; domains: CatalogDomain[] }> {\n // The bundled factory tree is injected into the repositories map as an implicit\n // source, so `am.search()` returns it too. It has its own `factory` origin band,\n // so drop it here — otherwise the same first-party skills list twice under Sources.\n const factoryPrefixes = factoryAssetRoots().map((r) => (r.endsWith(path.sep) ? r : r + path.sep));\n const isFactoryPath = (p: string) =>\n factoryPrefixes.some((fp) => p === fp.slice(0, -1) || p.startsWith(fp));\n\n const all = (await am.search()).filter((e) => !isFactoryPath(e.source));\n const entries: OriginEntry[] = [];\n const domains: CatalogDomain[] = [];\n // Group each source's entries under the source node (its short repo name), not\n // the publisher: under Sources the navigational question is \"which repo did\n // this come from\". Each entry's `source` (absolute manifest path) lives under\n // exactly one declared root, so group + stamp by prefix. The cache slug is\n // URL-keyed `github.com/<org>/<repo>` — take the trailing segment.\n for (const { repoName, root } of am.searchRepoRoots()) {\n if (isFactoryPath(root)) continue;\n const prefix = root.endsWith(path.sep) ? root : root + path.sep;\n const owned = all.filter((e) => e.source === root || e.source.startsWith(prefix));\n if (owned.length === 0) continue;\n const sourceName = repoName.split(\"/\").pop() || repoName;\n // No synthetic fallback domain — the source node already groups these, so a\n // same-named fallback would just nest the source under itself.\n domains.push(...(await stampDomains(root, sourceName, owned, false)));\n for (const entry of owned) entries.push({ origin: \"source\", entry, group: sourceName });\n }\n return { entries, domains };\n}\n\n/**\n * Factory-asset feed: scan the bundled first-party `factory-assets` tree(s)\n * resolved by `factoryAssetRoots()` (the `SKAILE_FACTORY_ASSETS_DIR` override\n * layer ahead of the bundled tree). Local disk only, no network — the entries are\n * the built-in skills / connectors / bundles that ship with `@skaile/workspaces`.\n * `factoryAssetRoots()` already dedupes, so the env override pointing at the\n * bundled dir collapses to one root.\n */\nasync function factoryAssets(): Promise<{\n entries: CatalogEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n const domains: CatalogDomain[] = [];\n try {\n const entries: CatalogEntry[] = [];\n for (const root of factoryAssetRoots()) {\n const rootEntries = [...(await scanRepo(root, \"skaile-factory\"))];\n domains.push(...(await stampDomains(root, \"skaile-factory\", rootEntries)));\n entries.push(...rootEntries);\n }\n return { entries, domains, notes };\n } catch (err) {\n notes.push({\n feed: \"factory\",\n message: err instanceof Error ? err.message : String(err),\n });\n return { entries: [], domains, notes };\n }\n}\n\n/** Tag a flat entry list with a single origin. */\nfunction tag(origin: AssetOrigin, entries: CatalogEntry[]): OriginEntry[] {\n return entries.map((entry) => ({ origin, entry }));\n}\n\n/**\n * Run all feeds and return a single normalized result.\n *\n * Feeds run in parallel where possible — the store fetch is the slow one\n * (one HTTP call) and is fired alongside the local library walk. The factory\n * scan is local disk only, so it runs inline.\n */\nexport async function gatherAssetFeeds(\n am: AssetManagerLike,\n projectDir: string,\n): Promise<AssetFeedResult> {\n const [libs, store] = await Promise.all([libraryAssets(), storeAssets(projectDir)]);\n const factory = await factoryAssets();\n const src = await sourceAssets(am);\n return {\n entries: [\n ...src.entries,\n ...libs.entries,\n ...tag(\"factory\", factory.entries),\n ...tag(\"store\", store.entries),\n ],\n // Store domains first (server-curated), then the on-disk feeds; dedupe by\n // slug, first-wins, so a store-served domain shadows a same-slug local one.\n domains: dedupeBySlug([...store.domains, ...src.domains, ...libs.domains, ...factory.domains]),\n notes: [...libs.notes, ...store.notes, ...factory.notes],\n };\n}\n\n/** First-wins dedupe of a domain list by `slug`. */\nfunction dedupeBySlug(domains: CatalogDomain[]): CatalogDomain[] {\n const seen = new Set<string>();\n const out: CatalogDomain[] = [];\n for (const d of domains) {\n if (seen.has(d.slug)) continue;\n seen.add(d.slug);\n out.push(d);\n }\n return out;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../cli/src/asset-feeds.ts"],"names":[],"mappings":"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","file":"asset-feeds-4AWAT5PW.js","sourcesContent":["/**\n * asset-feeds.ts — gather installable assets from every feed the manage TUI\n * surfaces.\n *\n * Four feeds, normalized into `CatalogEntry`:\n * - **Project sources** — `skaile.yaml` `sources:` entries (scanned via\n * `AssetManager.search()`; the new `sources:` field is merged into the\n * legacy repositories map in `AssetManager.loadConfig`).\n * - **Local libraries** — user authoring places from `LocalIndex` (filesystem\n * scan of each library's `path`).\n * - **Factory assets** — the bundled first-party `factory-assets` tree\n * (`factoryAssetRoots()`), scanned from disk. Always present; no network.\n * - **Remote store** — the configured catalog source when `catalog.url` is\n * not `local`. Failures are non-fatal — they surface as notes.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport {\n type AssetKind,\n type CatalogDomain,\n type CatalogEntry,\n factoryAssetRoots,\n parseFrontmatter,\n scanRepo,\n walkDir,\n} from \"@skaile/workspaces/core\";\nimport {\n assignDomain,\n buildDomainIndex,\n DOMAIN_MD_FILENAME,\n type DomainMd,\n parseDomainMd,\n} from \"@skaile/workspaces/discovery\";\n\n/** One non-fatal note returned to the caller for surfacing in the UI. */\nexport interface FeedNote {\n feed: \"sources\" | \"libraries\" | \"store\" | \"factory\";\n message: string;\n}\n\n/**\n * Which feed an asset came from. This is the top-level grouping axis in the\n * manage TUI (\"where is this skill coming from?\") and is intentionally distinct\n * from `publisher` (asset-intrinsic identity): the same publisher can legitimately\n * appear under more than one origin (e.g. a github repo you added as a `source`\n * that is also listed in the remote `store`).\n */\nexport type AssetOrigin = \"source\" | \"library\" | \"factory\" | \"store\";\n\n/** A catalog entry tagged with the feed it was gathered from. */\nexport interface OriginEntry {\n origin: AssetOrigin;\n entry: CatalogEntry;\n /**\n * Second-level grouping label for the manage tree. Defaults (when unset) to the\n * entry's `publisher`; the source feed sets it to the source's short repo name\n * so the Sources band groups by source — \"which repo did this come from\" — not\n * by the asset-intrinsic publisher.\n */\n group?: string;\n}\n\nexport interface AssetFeedResult {\n entries: OriginEntry[];\n /**\n * Display-only domain metadata, keyed (by the consumer) on `slug`. Populated\n * from the store's `catalog.listDomains` when the connected catalog source\n * exposes it; empty otherwise. The manage TUI joins `entry.domainSlug` → a\n * domain for grouping headers, descriptions, and relation badges.\n */\n domains: CatalogDomain[];\n notes: FeedNote[];\n}\n\n// ── Domain stamping (on-disk feeds) ──────────────────────────────────────────\n//\n// `scanRepo` (the source / library / factory scan path) does not read the\n// `DOMAIN.md` sidecars, so its entries arrive with no `domainSlug` and render\n// flat under their publisher band. We re-derive the grouping here from the\n// root's `DOMAIN.md` files (the same sidecars the remote store computes\n// server-side), and fall back to the repo/source name for any entry no domain\n// claims so it still groups instead of landing in the synthetic \"no domain\" pile.\n\n/** Repo-relative POSIX path (DOMAIN.md slugs are matched whole-segment, `/`-joined). */\nfunction toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\n/** Parse every `DOMAIN.md` sidecar under `root` into a {@link DomainMd}. */\nasync function loadDomainMds(root: string): Promise<DomainMd[]> {\n const out: DomainMd[] = [];\n for await (const { path: filePath, name } of walkDir(root)) {\n if (name !== DOMAIN_MD_FILENAME) continue;\n try {\n const { data } = parseFrontmatter(readFileSync(filePath, \"utf8\"));\n out.push(parseDomainMd(toPosix(path.relative(root, filePath)), data));\n } catch {}\n }\n return out;\n}\n\n/**\n * Stamp `domainSlug` onto every entry scanned from `root` using the root's\n * `DOMAIN.md` sidecars. Returns the `CatalogDomain` registry for the real\n * domains, plus a synthetic fallback domain when any entry used the fallback.\n * Mutates `entries` in place (sets `entry.domainSlug`).\n *\n * `synthesizeFallback` (default true): entries no domain claims fall back to\n * `fallbackSlug` (the repo/source name). Pass `false` when the caller already\n * groups these entries one level up (e.g. the source feed groups by source\n * node) — unmatched entries then get no domain (`\"\"`) and hang directly under\n * that parent, instead of nesting under a same-named fallback domain.\n */\nasync function stampDomains(\n root: string,\n fallbackSlug: string,\n entries: CatalogEntry[],\n synthesizeFallback = true,\n): Promise<CatalogDomain[]> {\n const domainMds = await loadDomainMds(root);\n const assignables = entries.map((entry) => ({\n entry,\n ref: `${entry.kind}:${entry.name}`,\n relativePath: toPosix(path.relative(root, entry.source)),\n }));\n\n let usedFallback = false;\n for (const a of assignables) {\n const slug = assignDomain(a.relativePath, domainMds);\n if (slug) a.entry.domainSlug = slug;\n else if (synthesizeFallback) {\n a.entry.domainSlug = fallbackSlug;\n usedFallback = true;\n } else a.entry.domainSlug = \"\";\n }\n\n const { index } = buildDomainIndex(assignables, domainMds);\n const catalog: CatalogDomain[] = Object.values(index).map((d) => ({\n slug: d.slug,\n title: d.title,\n ...(d.description ? { description: d.description } : {}),\n ...(d.relation ? { relation: d.relation } : {}),\n ...(d.order !== undefined ? { order: d.order } : {}),\n ...(d.recommended[0] ? { recommendedRef: d.recommended[0] } : {}),\n }));\n if (usedFallback) catalog.push({ slug: fallbackSlug, title: fallbackSlug });\n return catalog;\n}\n\n/**\n * Pull assets from local libraries (the authoring places under\n * `~/.skaile/libraries/<name>/`, not the `~/.skaile/cache/sources/` cache). Each\n * library's `path` is scanned with `scanRepo` and grouped under the bare library\n * name (`group: <name>`) so the manage TUI shows a clean `<name>` band — no\n * `library:` prefix and no redundant same-named fallback domain (mirrors the\n * source feed).\n */\nasync function libraryAssets(): Promise<{\n entries: OriginEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n const domains: CatalogDomain[] = [];\n try {\n const [{ openLibraryManager }, { skaileHomeDir }, { getGlobalCacheDir }] = await Promise.all([\n import(\"./open-library.ts\"),\n import(\"@skaile/workspaces/library\"),\n import(\"@skaile/workspaces/core\"),\n ]);\n const { manager, close } = await openLibraryManager();\n try {\n // Authoring libraries are the rows that are NOT source clones. index.db\n // rows predating the cache migration still carry the legacy\n // ~/.skaile/sources path, so exclude both roots.\n const newRoot = getGlobalCacheDir();\n const legacyRoot = path.join(skaileHomeDir(), \"sources\");\n const libs = (await manager.listLibraries()).filter(\n (l) => !l.path.startsWith(newRoot) && !l.path.startsWith(legacyRoot),\n );\n const entries: OriginEntry[] = [];\n for (const lib of libs) {\n if (!existsSync(lib.path)) continue;\n // Scan + group under the bare library name. `synthesizeFallback: false`\n // (as the source feed does) keeps domainless assets directly under the\n // library band instead of a redundant same-named fallback domain.\n const libEntries = [...(await scanRepo(lib.path, lib.name))];\n domains.push(...(await stampDomains(lib.path, lib.name, libEntries, false)));\n for (const entry of libEntries) entries.push({ origin: \"library\", entry, group: lib.name });\n }\n return { entries, domains, notes };\n } finally {\n close();\n }\n } catch (err) {\n notes.push({\n feed: \"libraries\",\n message: err instanceof Error ? err.message : String(err),\n });\n return { entries: [], domains, notes };\n }\n}\n\n/**\n * Pull a (best-effort) snapshot of the connected remote store catalog.\n *\n * - Returns no entries when the resolved catalog is local-only or the host\n * is unreachable (the failure becomes a note, not an exception).\n * - Maps `CatalogAsset` → `CatalogEntry`, carrying `publisher`, `repo`, and\n * `category` straight through and using `source = \"store://<id>\"`; per-file\n * `source` paths are not available for remote assets so the canonical ref is\n * a stable placeholder.\n */\nasync function storeAssets(projectDir: string): Promise<{\n entries: CatalogEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n try {\n const { resolveCatalogSource } = await import(\"./open-library.ts\");\n const { source, close } = await resolveCatalogSource({ projectDir });\n try {\n const assets = await source.listAssets();\n const entries: CatalogEntry[] = assets.map((a) => ({\n name: a.name,\n kind: (a.kind as AssetKind) ?? \"skill\",\n description: a.description ?? \"\",\n source: `store://${a.id}`,\n publisher: a.publisher,\n repo: a.repo,\n domainSlug: a.domainSlug,\n category: a.category,\n version: a.version,\n requires: [],\n dependencies: [],\n }));\n // Domains are optional on the catalog source — only the store backend\n // (post Part 4 `catalog.listDomains`) exposes them. Duck-type the call so\n // older sources and the local catalog degrade to no domains, not an error.\n const domains = await loadDomains(source);\n return { entries, domains, notes };\n } finally {\n close();\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // Local-only catalog throws a recognizable message — silence it; absence\n // is the user's choice, not an error.\n if (!/local/i.test(msg)) {\n notes.push({ feed: \"store\", message: msg });\n }\n return { entries: [], domains: [], notes };\n }\n}\n\n/** Optional `listDomains` capability — returns `[]` when the source lacks it. */\nasync function loadDomains(source: unknown): Promise<CatalogDomain[]> {\n const fn = (source as { listDomains?: () => Promise<CatalogDomain[]> }).listDomains;\n if (typeof fn !== \"function\") return [];\n const domains = await fn.call(source);\n return Array.isArray(domains) ? domains : [];\n}\n\n/** The slice of `AssetManager` the source feed needs (kept narrow for tests). */\ninterface AssetManagerLike {\n search: () => Promise<CatalogEntry[]>;\n searchRepoRoots: () => { repoName: string; root: string }[];\n}\n\n/**\n * Project-source feed: leans on `AssetManager.search()`, which now merges\n * `skaile.yaml`'s `sources:` field into the repositories map, then stamps\n * `DOMAIN.md`-derived domain grouping onto the scanned entries via\n * `searchRepoRoots()`.\n */\nasync function sourceAssets(\n am: AssetManagerLike,\n): Promise<{ entries: OriginEntry[]; domains: CatalogDomain[] }> {\n // The bundled factory tree is injected into the repositories map as an implicit\n // source, so `am.search()` returns it too. It has its own `factory` origin band,\n // so drop it here — otherwise the same first-party skills list twice under Sources.\n const factoryPrefixes = factoryAssetRoots().map((r) => (r.endsWith(path.sep) ? r : r + path.sep));\n const isFactoryPath = (p: string) =>\n factoryPrefixes.some((fp) => p === fp.slice(0, -1) || p.startsWith(fp));\n\n const all = (await am.search()).filter((e) => !isFactoryPath(e.source));\n const entries: OriginEntry[] = [];\n const domains: CatalogDomain[] = [];\n // Group each source's entries under the source node (its short repo name), not\n // the publisher: under Sources the navigational question is \"which repo did\n // this come from\". Each entry's `source` (absolute manifest path) lives under\n // exactly one declared root, so group + stamp by prefix. The cache slug is\n // URL-keyed `github.com/<org>/<repo>` — take the trailing segment.\n for (const { repoName, root } of am.searchRepoRoots()) {\n if (isFactoryPath(root)) continue;\n const prefix = root.endsWith(path.sep) ? root : root + path.sep;\n const owned = all.filter((e) => e.source === root || e.source.startsWith(prefix));\n if (owned.length === 0) continue;\n const sourceName = repoName.split(\"/\").pop() || repoName;\n // No synthetic fallback domain — the source node already groups these, so a\n // same-named fallback would just nest the source under itself.\n domains.push(...(await stampDomains(root, sourceName, owned, false)));\n for (const entry of owned) entries.push({ origin: \"source\", entry, group: sourceName });\n }\n return { entries, domains };\n}\n\n/**\n * Factory-asset feed: scan the bundled first-party `factory-assets` tree(s)\n * resolved by `factoryAssetRoots()` (the `SKAILE_FACTORY_ASSETS_DIR` override\n * layer ahead of the bundled tree). Local disk only, no network — the entries are\n * the built-in skills / connectors / bundles that ship with `@skaile/workspaces`.\n * `factoryAssetRoots()` already dedupes, so the env override pointing at the\n * bundled dir collapses to one root.\n */\nasync function factoryAssets(): Promise<{\n entries: CatalogEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n const domains: CatalogDomain[] = [];\n try {\n const entries: CatalogEntry[] = [];\n for (const root of factoryAssetRoots()) {\n const rootEntries = [...(await scanRepo(root, \"skaile-factory\"))];\n domains.push(...(await stampDomains(root, \"skaile-factory\", rootEntries)));\n entries.push(...rootEntries);\n }\n return { entries, domains, notes };\n } catch (err) {\n notes.push({\n feed: \"factory\",\n message: err instanceof Error ? err.message : String(err),\n });\n return { entries: [], domains, notes };\n }\n}\n\n/** Tag a flat entry list with a single origin. */\nfunction tag(origin: AssetOrigin, entries: CatalogEntry[]): OriginEntry[] {\n return entries.map((entry) => ({ origin, entry }));\n}\n\n/**\n * Run all feeds and return a single normalized result.\n *\n * Feeds run in parallel where possible — the store fetch is the slow one\n * (one HTTP call) and is fired alongside the local library walk. The factory\n * scan is local disk only, so it runs inline.\n */\nexport async function gatherAssetFeeds(\n am: AssetManagerLike,\n projectDir: string,\n): Promise<AssetFeedResult> {\n const [libs, store] = await Promise.all([libraryAssets(), storeAssets(projectDir)]);\n const factory = await factoryAssets();\n const src = await sourceAssets(am);\n return {\n entries: [\n ...src.entries,\n ...libs.entries,\n ...tag(\"factory\", factory.entries),\n ...tag(\"store\", store.entries),\n ],\n // Store domains first (server-curated), then the on-disk feeds; dedupe by\n // slug, first-wins, so a store-served domain shadows a same-slug local one.\n domains: dedupeBySlug([...store.domains, ...src.domains, ...libs.domains, ...factory.domains]),\n notes: [...libs.notes, ...store.notes, ...factory.notes],\n };\n}\n\n/** First-wins dedupe of a domain list by `slug`. */\nfunction dedupeBySlug(domains: CatalogDomain[]): CatalogDomain[] {\n const seen = new Set<string>();\n const out: CatalogDomain[] = [];\n for (const d of domains) {\n if (seen.has(d.slug)) continue;\n seen.add(d.slug);\n out.push(d);\n }\n return out;\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { WORKSPACE_MOUNT_ENV_VAR, deployAll, deployCatalogEntry, findAiAssetsRoot, isUnderWorkspaceMount, resolveFlowSkills, undeployCatalogEntry, workspaceMountDirs } from '../chunk-BEAIJRVF.js';
|
|
2
|
-
import '../chunk-
|
|
2
|
+
import '../chunk-P43HBWKK.js';
|
|
3
3
|
import '../chunk-F3HNW354.js';
|
|
4
4
|
import '../chunk-K5GBV4SA.js';
|
|
5
5
|
import '../chunk-ZAP2BDHG.js';
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
export { AssetManager, FACTORY_SOURCE_URL, MissingManifestError, NON_DEPLOYABLE_KINDS, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, renderMcpServerConfig, softDepName } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
3
|
-
import '../chunk-
|
|
1
|
+
export { AssetManager, FACTORY_SOURCE_URL, MissingManifestError, NON_DEPLOYABLE_KINDS, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, renderMcpServerConfig, softDepName } from '../chunk-WSARNOPP.js';
|
|
2
|
+
import '../chunk-3HVASHBF.js';
|
|
3
|
+
import '../chunk-NU4E7C5I.js';
|
|
4
4
|
import '../chunk-UKJMQZV6.js';
|
|
5
5
|
import '../chunk-R7FOF242.js';
|
|
6
6
|
import '../chunk-JXT46QFC.js';
|
|
7
|
-
export { createScaffold, deployAll, installAgent, removeAsset } from '../chunk-
|
|
7
|
+
export { createScaffold, deployAll, installAgent, removeAsset } from '../chunk-2OCYS5EP.js';
|
|
8
8
|
export { BUILT_IN_FRAGMENTS, loadPromptExtensions, resolveFragments } from '../chunk-CGYEHQOX.js';
|
|
9
|
-
import '../chunk-
|
|
9
|
+
import '../chunk-P43HBWKK.js';
|
|
10
10
|
import '../chunk-F3HNW354.js';
|
|
11
11
|
import '../chunk-K5GBV4SA.js';
|
|
12
12
|
import '../chunk-ZAP2BDHG.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { createScaffold, deployAll, deployAsset, removeAsset } from '../chunk-
|
|
1
|
+
export { createScaffold, deployAll, deployAsset, removeAsset } from '../chunk-2OCYS5EP.js';
|
|
2
2
|
import '../chunk-CGYEHQOX.js';
|
|
3
|
-
import '../chunk-
|
|
3
|
+
import '../chunk-P43HBWKK.js';
|
|
4
4
|
import '../chunk-F3HNW354.js';
|
|
5
5
|
import '../chunk-K5GBV4SA.js';
|
|
6
6
|
import '../chunk-ZAP2BDHG.js';
|
|
@@ -2,9 +2,9 @@ import { SUPPORTED_CLOUDS, buildClaudeCloudEnv } from '../../chunk-ISQWMBKD.js';
|
|
|
2
2
|
import { dispatchCapability } from '../../chunk-RRVQAE5D.js';
|
|
3
3
|
import { fetchProviderModels } from '../../chunk-BMQKMOQY.js';
|
|
4
4
|
import { AuthError, classifyClaudeSdkError, reclassifyAuthAsAccountUnavailable, AccountUnavailableError } from '../../chunk-GAH4YRCI.js';
|
|
5
|
-
import { BUILTIN_DRIVER_CATALOG, AgentDriver, getBridgeLogger } from '../../chunk-
|
|
5
|
+
import { BUILTIN_DRIVER_CATALOG, AgentDriver, getBridgeLogger } from '../../chunk-HY3MBSNC.js';
|
|
6
6
|
import '../../chunk-6E6PKKAD.js';
|
|
7
|
-
import '../../chunk-
|
|
7
|
+
import '../../chunk-P43HBWKK.js';
|
|
8
8
|
import '../../chunk-F3HNW354.js';
|
|
9
9
|
import '../../chunk-K5GBV4SA.js';
|
|
10
10
|
import '../../chunk-ZAP2BDHG.js';
|
|
@@ -17,8 +17,8 @@ import '../../chunk-GEEW6SY2.js';
|
|
|
17
17
|
import '../../chunk-U5POE4BA.js';
|
|
18
18
|
import '../../chunk-7GOCNYUV.js';
|
|
19
19
|
import '../../chunk-NSBPE2FW.js';
|
|
20
|
-
import { spawnSync } from 'child_process';
|
|
21
|
-
import { existsSync, createReadStream, createWriteStream } from 'fs';
|
|
20
|
+
import { spawnSync, spawn } from 'child_process';
|
|
21
|
+
import { readFileSync, existsSync, createReadStream, createWriteStream } from 'fs';
|
|
22
22
|
import { createRequire } from 'module';
|
|
23
23
|
import { homedir } from 'os';
|
|
24
24
|
import { join } from 'path';
|
|
@@ -393,6 +393,12 @@ var ClaudeSdkDriver = class extends AgentDriver {
|
|
|
393
393
|
suppressingAuthStream = false;
|
|
394
394
|
/** Active query — may stay alive between turns if streamInput works. */
|
|
395
395
|
query = null;
|
|
396
|
+
/**
|
|
397
|
+
* OS pid of the CLI subprocess backing `query`, captured via
|
|
398
|
+
* `spawnClaudeCodeProcess` in {@link startQuery}. Feeds
|
|
399
|
+
* {@link snapshotDescendantPids}; see `_devlog/notes/2026-08-12-abort-kills-tool-subprocess.md`.
|
|
400
|
+
*/
|
|
401
|
+
cliProcessPid = null;
|
|
396
402
|
/** Resolves when the current turn completes. */
|
|
397
403
|
turnResolve = null;
|
|
398
404
|
/** Rejects when the current turn errors. */
|
|
@@ -629,7 +635,9 @@ var ClaudeSdkDriver = class extends AgentDriver {
|
|
|
629
635
|
onTurnStalled(idleMs) {
|
|
630
636
|
const idle = idleMs < 6e4 ? `${Math.round(idleMs / 1e3)}s` : `${Math.round(idleMs / 6e4)}m`;
|
|
631
637
|
this.log.error("turn stalled \u2014 no SDK message received; resetting the query", { idleMs });
|
|
638
|
+
const descendants = this.snapshotDescendantPids();
|
|
632
639
|
this.hardResetQuery();
|
|
640
|
+
this.killPids(descendants);
|
|
633
641
|
this.failTurn(
|
|
634
642
|
new Error(
|
|
635
643
|
`Agent stalled: no response from the model process for ${idle}. The conversation is intact \u2014 send your message again.`
|
|
@@ -642,6 +650,10 @@ var ClaudeSdkDriver = class extends AgentDriver {
|
|
|
642
650
|
* Deliberately does NOT clear `sessionId` / `hasSession` — the next
|
|
643
651
|
* `startQuery()` must resume the same SDK session so the conversation
|
|
644
652
|
* survives. That is what separates this from {@link kill}.
|
|
653
|
+
*
|
|
654
|
+
* Does NOT sweep the CLI's descendant processes — callers needing that must
|
|
655
|
+
* snapshot via {@link snapshotDescendantPids} *before* this (and any other
|
|
656
|
+
* abort signal) runs, then {@link killPids} after.
|
|
645
657
|
*/
|
|
646
658
|
hardResetQuery() {
|
|
647
659
|
if (this.query) {
|
|
@@ -653,6 +665,60 @@ var ClaudeSdkDriver = class extends AgentDriver {
|
|
|
653
665
|
}
|
|
654
666
|
this.abortController?.abort();
|
|
655
667
|
}
|
|
668
|
+
/**
|
|
669
|
+
* Capture every current OS descendant of the CLI subprocess (SIGKILLed
|
|
670
|
+
* later via {@link killPids}). **Call BEFORE `interrupt()` /
|
|
671
|
+
* `abortController.abort()` / `query.close()` — never after**: once the CLI
|
|
672
|
+
* exits, Linux reparents its children immediately, so a later walk finds
|
|
673
|
+
* nothing. Linux-only; `[]` if there's no captured pid or no `/proc`. Full
|
|
674
|
+
* rationale: `_devlog/notes/2026-08-12-abort-kills-tool-subprocess.md`.
|
|
675
|
+
*/
|
|
676
|
+
snapshotDescendantPids() {
|
|
677
|
+
const rootPid = this.cliProcessPid;
|
|
678
|
+
if (rootPid == null) return [];
|
|
679
|
+
return this.collectDescendantPids(rootPid);
|
|
680
|
+
}
|
|
681
|
+
/** SIGKILL every pid in a snapshot from {@link snapshotDescendantPids}. */
|
|
682
|
+
killPids(pids) {
|
|
683
|
+
if (pids.length === 0) return;
|
|
684
|
+
let killed = 0;
|
|
685
|
+
for (const pid of pids) {
|
|
686
|
+
try {
|
|
687
|
+
process.kill(pid, "SIGKILL");
|
|
688
|
+
killed++;
|
|
689
|
+
} catch {
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
this.log.info("swept CLI descendant processes on abort", {
|
|
693
|
+
attempted: pids.length,
|
|
694
|
+
killed
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
/** Direct children of `pid` via Linux procfs — no external binary required. */
|
|
698
|
+
childPids(pid) {
|
|
699
|
+
try {
|
|
700
|
+
const raw = readFileSync(`/proc/${pid}/task/${pid}/children`, "utf8");
|
|
701
|
+
return raw.trim().split(/\s+/).filter(Boolean).map(Number);
|
|
702
|
+
} catch {
|
|
703
|
+
return [];
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
/** Every descendant of `rootPid` (excluding `rootPid` itself), breadth-first. */
|
|
707
|
+
collectDescendantPids(rootPid) {
|
|
708
|
+
const descendants = [];
|
|
709
|
+
let frontier = [rootPid];
|
|
710
|
+
while (frontier.length > 0) {
|
|
711
|
+
const next = [];
|
|
712
|
+
for (const pid of frontier) {
|
|
713
|
+
for (const child of this.childPids(pid)) {
|
|
714
|
+
descendants.push(child);
|
|
715
|
+
next.push(child);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
frontier = next;
|
|
719
|
+
}
|
|
720
|
+
return descendants;
|
|
721
|
+
}
|
|
656
722
|
/**
|
|
657
723
|
* Side-effects of the stale-resume recovery path: emit the resume-outcome and
|
|
658
724
|
* non-fatal error events, then reset session state so the recursive prompt()
|
|
@@ -850,6 +916,7 @@ var ClaudeSdkDriver = class extends AgentDriver {
|
|
|
850
916
|
const apiKey = cloud === "default" ? this.config.apiKeys?.anthropic || this.config.env?.ANTHROPIC_API_KEY || process.env.ANTHROPIC_API_KEY : void 0;
|
|
851
917
|
this.usingOauthCredential = cloud === "default" && !apiKey;
|
|
852
918
|
const claudePath = this.findClaudeBinary();
|
|
919
|
+
const onStderr = (data) => this.log.warn("claude stderr", { line: data.trim() });
|
|
853
920
|
const options = {
|
|
854
921
|
cwd: this.config.cwd,
|
|
855
922
|
// Load project + user settings so .claude/skills/ are discovered
|
|
@@ -872,7 +939,29 @@ var ClaudeSdkDriver = class extends AgentDriver {
|
|
|
872
939
|
// Reasoning effort — passed directly to the SDK
|
|
873
940
|
...this.config.effort ? { effort: this.config.effort } : {},
|
|
874
941
|
hooks: {},
|
|
875
|
-
stderr:
|
|
942
|
+
stderr: onStderr,
|
|
943
|
+
// Spawn the CLI ourselves to capture its OS pid for snapshotDescendantPids()
|
|
944
|
+
// (Query never exposes it otherwise). This replaces the SDK's own default
|
|
945
|
+
// spawn, so we also have to re-wire child.stderr -> onStderr ourselves below.
|
|
946
|
+
spawnClaudeCodeProcess: (spawnOpts) => {
|
|
947
|
+
const child = spawn(spawnOpts.command, spawnOpts.args, {
|
|
948
|
+
cwd: spawnOpts.cwd,
|
|
949
|
+
env: spawnOpts.env,
|
|
950
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
951
|
+
});
|
|
952
|
+
this.cliProcessPid = child.pid ?? null;
|
|
953
|
+
child.stderr?.on("data", (chunk) => onStderr(chunk.toString()));
|
|
954
|
+
child.once("exit", () => {
|
|
955
|
+
if (this.cliProcessPid === child.pid) this.cliProcessPid = null;
|
|
956
|
+
});
|
|
957
|
+
spawnOpts.signal.addEventListener("abort", () => {
|
|
958
|
+
try {
|
|
959
|
+
child.kill();
|
|
960
|
+
} catch {
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
return child;
|
|
964
|
+
},
|
|
876
965
|
...claudePath ? { pathToClaudeCodeExecutable: claudePath } : {},
|
|
877
966
|
...this.config.systemPrompt ? {
|
|
878
967
|
systemPrompt: {
|
|
@@ -1542,6 +1631,10 @@ var ClaudeSdkDriver = class extends AgentDriver {
|
|
|
1542
1631
|
* Requests the current SDK query to stop processing via `query.interrupt()` and
|
|
1543
1632
|
* signals the underlying `AbortController`. The driver remains usable after abort.
|
|
1544
1633
|
*
|
|
1634
|
+
* Also SIGKILLs every OS descendant of the CLI subprocess (e.g. a running
|
|
1635
|
+
* Bash command) on either path the turn ends — see
|
|
1636
|
+
* {@link snapshotDescendantPids}'s doc for why that's needed.
|
|
1637
|
+
*
|
|
1545
1638
|
* @remarks Best-effort: if `interrupt()` throws (e.g. query already ended) the
|
|
1546
1639
|
* error is silently swallowed and the abort signal is still sent. A query
|
|
1547
1640
|
* whose generator refuses to unwind within {@link ABORT_GRACE_MS} is torn
|
|
@@ -1551,6 +1644,7 @@ var ClaudeSdkDriver = class extends AgentDriver {
|
|
|
1551
1644
|
this.denyPendingQuestion("turn aborted before the question was answered");
|
|
1552
1645
|
const abortedQuery = this.query;
|
|
1553
1646
|
const abortedController = this.abortController;
|
|
1647
|
+
const descendants = this.snapshotDescendantPids();
|
|
1554
1648
|
if (abortedQuery) {
|
|
1555
1649
|
try {
|
|
1556
1650
|
await abortedQuery.interrupt();
|
|
@@ -1569,6 +1663,7 @@ var ClaudeSdkDriver = class extends AgentDriver {
|
|
|
1569
1663
|
this.hardResetQuery();
|
|
1570
1664
|
this.completeTurn();
|
|
1571
1665
|
}
|
|
1666
|
+
this.killPids(descendants);
|
|
1572
1667
|
}
|
|
1573
1668
|
/** `true` while the driver is processing a `prompt()` call. */
|
|
1574
1669
|
get isRunning() {
|
|
@@ -1579,9 +1674,14 @@ var ClaudeSdkDriver = class extends AgentDriver {
|
|
|
1579
1674
|
*
|
|
1580
1675
|
* The driver instance must not be reused after `kill()`. Session IDs are cleared,
|
|
1581
1676
|
* so a new `ClaudeSdkDriver` will start a fresh session.
|
|
1677
|
+
*
|
|
1678
|
+
* Also SIGKILLs every OS descendant of the CLI subprocess (e.g. a running
|
|
1679
|
+
* Bash command) — see {@link snapshotDescendantPids}'s doc for why that
|
|
1680
|
+
* needs to be snapshotted before `query.close()` runs, not after.
|
|
1582
1681
|
*/
|
|
1583
1682
|
kill() {
|
|
1584
1683
|
this.denyPendingQuestion("session terminated before the question was answered");
|
|
1684
|
+
const descendants = this.snapshotDescendantPids();
|
|
1585
1685
|
if (this.query) {
|
|
1586
1686
|
try {
|
|
1587
1687
|
this.query.close();
|
|
@@ -1590,6 +1690,7 @@ var ClaudeSdkDriver = class extends AgentDriver {
|
|
|
1590
1690
|
this.query = null;
|
|
1591
1691
|
}
|
|
1592
1692
|
this.abortController?.abort();
|
|
1693
|
+
this.killPids(descendants);
|
|
1593
1694
|
this.running = false;
|
|
1594
1695
|
this.sessionId = "";
|
|
1595
1696
|
this.hasSession = false;
|
|
@@ -1609,6 +1710,7 @@ var ClaudeSdkDriver = class extends AgentDriver {
|
|
|
1609
1710
|
}
|
|
1610
1711
|
async resetSession() {
|
|
1611
1712
|
this.denyPendingQuestion("session reset before the question was answered");
|
|
1713
|
+
const descendants = this.snapshotDescendantPids();
|
|
1612
1714
|
if (this.query) {
|
|
1613
1715
|
try {
|
|
1614
1716
|
this.query.close();
|
|
@@ -1617,6 +1719,7 @@ var ClaudeSdkDriver = class extends AgentDriver {
|
|
|
1617
1719
|
this.query = null;
|
|
1618
1720
|
}
|
|
1619
1721
|
this.abortController?.abort();
|
|
1722
|
+
this.killPids(descendants);
|
|
1620
1723
|
this.sessionId = "";
|
|
1621
1724
|
this.hasSession = false;
|
|
1622
1725
|
this._lastTokens = null;
|