svamp-cli 0.2.342 → 0.2.356

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/bin/skills/artifact/SKILL.md +19 -5
  2. package/bin/skills/crew/SKILL.md +62 -13
  3. package/dist/DefaultTransport-vfp78fug.mjs +47 -0
  4. package/dist/GeminiTransport-3OkhtFRO.mjs +130 -0
  5. package/dist/KimiTransport-D8n009Tr.mjs +141 -0
  6. package/dist/acpAgentConfig-CObOPlA8.mjs +48 -0
  7. package/dist/acpBackend-UaBD4zL3.mjs +925 -0
  8. package/dist/{adminCommands-CRTBDZan.mjs → adminCommands-BvsYnRDA.mjs} +2 -18
  9. package/dist/{agentCommands-BuO988tJ.mjs → agentCommands-D91RcjqQ.mjs} +6 -23
  10. package/dist/api-BHjyC0Ph.mjs +157 -0
  11. package/dist/{auth--WS0wezs.mjs → auth-CRz8TsEl.mjs} +1 -22
  12. package/dist/bootStatus-DqtNgJch.mjs +143 -0
  13. package/dist/claudeAuth-C0wc1GTM.mjs +291 -0
  14. package/dist/cli.mjs +2948 -5
  15. package/dist/codexAppServerBackend-VGsYtbqg.mjs +786 -0
  16. package/dist/codexProvider-BtxYc5Rj.mjs +68 -0
  17. package/dist/{commands-DDuGBSsz.mjs → commands-B-yczgz_.mjs} +115 -49
  18. package/dist/{commands-DU4B-4Gh.mjs → commands-BMeDrpSv.mjs} +55 -25
  19. package/dist/{commands-DXLOKJOj.mjs → commands-BbmypSgx.mjs} +62 -27
  20. package/dist/{commands-C0HQiu-S.mjs → commands-BkdbelFo.mjs} +78 -38
  21. package/dist/{commands-DXM17M6n.mjs → commands-C13-yE2R.mjs} +42 -19
  22. package/dist/{commands-BKb1g8Gq.mjs → commands-CoOc4qiw.mjs} +20 -30
  23. package/dist/{commands-BsKgZ5sQ.mjs → commands-DW5RnHZw.mjs} +2 -19
  24. package/dist/{commands-Cwxy-C5N.mjs → commands-sPvRcG3J.mjs} +10 -32
  25. package/dist/cron-hHGdb5kU.mjs +132 -0
  26. package/dist/detectIsolation-CrV8IFDv.mjs +339 -0
  27. package/dist/envFlag-COOuJ3AH.mjs +11 -0
  28. package/dist/fileLock-BMPwCrB6.mjs +80 -0
  29. package/dist/{fleet-BHzlAvnu.mjs → fleet-Ca-UjJNL.mjs} +122 -36
  30. package/dist/flushExit-C-i6SGjj.mjs +37 -0
  31. package/dist/friendlyName-1UZcyDhu.mjs +586 -0
  32. package/dist/{headlessCli-C2gvOpOt.mjs → headlessCli-C8HzQvis.mjs} +3 -21
  33. package/dist/hookSettings-FFc1pi8j.mjs +10546 -0
  34. package/dist/{httpServer-Be1Hcddr.mjs → httpServer-BzEDbOGn.mjs} +14 -5
  35. package/dist/hyphaClient-ybivOjZo.mjs +49 -0
  36. package/dist/index.mjs +40 -8
  37. package/dist/instance-CeHz5C8v.mjs +203 -0
  38. package/dist/kimiProvider-DbxODebR.mjs +69 -0
  39. package/dist/kimiSetup-NGsoYB5h.mjs +48 -0
  40. package/dist/loopVerify-DALtTk2V.mjs +444 -0
  41. package/dist/machineTools-DygM1jXW.mjs +580 -0
  42. package/dist/model-CUHojegn.mjs +66 -0
  43. package/dist/{notifyCommands-0aZUwKQK.mjs → notifyCommands-CCUE18Q-.mjs} +3 -18
  44. package/dist/package-BrsiNutS.mjs +64 -0
  45. package/dist/pinnedCodex-XW4qvOAe.mjs +11 -0
  46. package/dist/pinnedNono-5ZHb6328.mjs +15 -0
  47. package/dist/{rpc-rAQQ1q8t.mjs → rpc-CBkSkzsI.mjs} +38 -6
  48. package/dist/{rpc-B2Lc2BAB.mjs → rpc-ccY7xvlo.mjs} +33 -22
  49. package/dist/{run-C7QVfyw4.mjs → run-DCq3aEiF.mjs} +17 -9
  50. package/dist/run-DDmeV9K3.mjs +9826 -0
  51. package/dist/sandboxDetect-Cdzsj5TC.mjs +19 -0
  52. package/dist/{scheduler-CzF4lOe_.mjs → scheduler-BkVJ27Ki.mjs} +38 -6
  53. package/dist/securityContext-Cm5UTCuD.mjs +218 -0
  54. package/dist/serveAuth-4M0vwMyj.mjs +460 -0
  55. package/dist/{serveCommands-EIEqdaVu.mjs → serveCommands-CLhkccJM.mjs} +13 -34
  56. package/dist/sharingNotificationSync-qGIQ-3yf.mjs +359 -0
  57. package/dist/{sideband-BoutiRJi.mjs → sideband-68HhGT0i.mjs} +1 -21
  58. package/dist/store-B4E6NzG6.mjs +356 -0
  59. package/dist/store-fi0f-ff1.mjs +184 -0
  60. package/dist/types-57J3Re0n.mjs +17 -0
  61. package/dist/wantsHelp-6bMOeNUz.mjs +5 -0
  62. package/dist/wrapCommand-CdNawSaa.mjs +117 -0
  63. package/package.json +3 -3
  64. package/dist/cli-B24CmIli.mjs +0 -2780
  65. package/dist/package-CTbGYaJG.mjs +0 -64
  66. package/dist/run-CF4kAVPW.mjs +0 -26508
  67. package/dist/sandboxDetect-DNTcbgWD.mjs +0 -12
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: artifact
3
- version: 1.3.0
3
+ version: 1.3.2
4
4
  description: Embed a rendered HTML file or live URL inline in chat via a single self-closing `<artifact src="..." />` tag — the Svamp client renders it as a sandboxed iframe with modes for inline, card, bare, and immersive.
5
5
  tags:
6
6
  - svamp
@@ -134,16 +134,30 @@ This means you can iterate freely without polluting the chat with stale renders.
134
134
  To share with a colleague, run `svamp serve`:
135
135
 
136
136
  ```bash
137
- # Owner-only (defaultthey need to log in via Hypha)
137
+ # DEFAULT is the `link` tier a capability URL. NO login required:
138
+ # anyone who has the URL can read it. The hostname IS the secret.
138
139
  svamp serve dashboard ./outputs
139
140
 
140
- # Public link (anyone with the URL)
141
- svamp serve dashboard ./outputs --public
141
+ # Login-gated (only you, via Hypha) — opt IN to this
142
+ svamp serve dashboard ./outputs --access owner
142
143
 
143
- # Restrict to specific emails
144
+ # Restrict to specific emails (they log in via Hypha)
144
145
  svamp serve dashboard ./outputs --access alice@x.com,bob@y.com
146
+
147
+ # Listed publicly (anyone, no URL secrecy assumed)
148
+ svamp serve dashboard ./outputs --public
145
149
  ```
146
150
 
151
+ ⚠️ **Do not assume the default is login-gated.** It is not — `svamp serve` prints the tier back to
152
+ you as `link (capability URL — anyone with the URL can view)`. Before serving anything internal,
153
+ either pass `--access owner` or accept that possession of the URL is possession of the content.
154
+ Because the hostname is the secret, there is no way to revoke access to a `link` mount other than
155
+ `--regenerate-url`, which mints a new one; deleting the mount does not un-publish what was already
156
+ fetched.
157
+
158
+ Use the URL exactly as `serve`/`serve list` prints it — never compose it by hand, and never append a
159
+ path to it.
160
+
147
161
  Post the resulting URL as a normal markdown link. The svamp client doesn't manage shares — `svamp serve` is the dedicated tool.
148
162
 
149
163
  ## Rendering environment
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: crew
3
- version: 0.3.0
3
+ version: 0.3.4
4
4
  description: Coordinate work as a lead delegating features to managed worktree children — spawn a child per feature on its own branch, run it as a loop until done, and pull finished work back to the base branch with merge-and-close. Use when one session should run a project and farm out features instead of piling many sessions on one working tree.
5
5
  ---
6
6
 
@@ -13,10 +13,27 @@ the same working tree.
13
13
 
14
14
  A **crew** is emergent, not a heavy object: it is just plain sessions linked by
15
15
  `parentSessionId` + a `crew.role`. A **feature child** always (a) has the lead as its
16
- parent, (b) lives in its own `.dev/worktree/<n>` on its own branch, and (c) runs as a loop
17
- with `parent: <lead>` — i.e. **the lead is the judge** that decides when the child is done
18
- (this is the `parent`-routed case of the loop until gate; the loop engine is Claude Code's
19
- native `/goal`, driven from `svamp session loop`).
16
+ parent and (b) lives in its own `.dev/worktree/<n>` on its own branch.
17
+
18
+ > **⚠ Whether the child is GATED depends on how you started it (#1604).**
19
+ >
20
+ > | You ran | Gate |
21
+ > |---|---|
22
+ > | `crew start "<brief>"` (no `--oracle`) | **none** — the child works, then runs `feature done` when *it* decides it is complete. Nothing judges it. |
23
+ > | `crew start "<brief>" --oracle "<cmd>"` | a loop with `parent: <lead>` — the oracle gates each stop and **the lead is the judge** of done. |
24
+ >
25
+ > The CLI tells you which you got: it prints `gate: none — child runs 'feature done' when complete`
26
+ > on the ungated path. This is deliberate, not an oversight — `featureStart` attaches the loop only
27
+ > when there is an oracle, because a no-oracle child would otherwise fire `approved` on its very
28
+ > first stop, which is worse than no gate at all.
29
+ >
30
+ > **What this means for you as a lead:** if the feature has any objective success condition, pass
31
+ > `--oracle`. Without one you are the only thing standing between a child and a premature
32
+ > `feature done`, so read the merge request rather than trusting the verdict.
33
+ >
34
+ > This block used to claim every feature child runs as a loop with the lead as judge. It did not,
35
+ > and an agent that believed it would skip `--oracle`, skip checking on the child, and read "no
36
+ > verdict yet" as "still working" rather than "nothing will ever judge this".
20
37
 
21
38
  ## The verbs (these are your tools)
22
39
 
@@ -25,10 +42,21 @@ svamp feature start "<brief>" [--oracle "<test cmd>"] [--base <branch>] [--actio
25
42
  [--issue <id>] [--max N] [--model <id>]
26
43
  # (lead) create a worktree child on a new branch, run it as a loop until done
27
44
  # (oracle → parent verdict), and brief it. Prints the child id.
28
- # --issue <id> links the child to an existing backlog issue: its verdict routes
29
- # ONTO that issue. This is the seam that joins crew to the backlog the loop
30
- # oracle (`svamp issue pending`) reads without it the child's result is
31
- # invisible to the loop, so pass it whenever the feature came from an issue.
45
+ # --action nudge|block: how the lead learns the child reported or finished.
46
+ # nudge (default) = normal urgency, the lead sees it on its next idle turn;
47
+ # block = urgent, which WAKES the lead. Use block for a child whose result
48
+ # gates your next step; nudge for one you will collect when convenient.
49
+ # --issue <id> links the child to an EXISTING backlog issue: its verdict routes
50
+ # ONTO that issue instead of onto a new one. This is the seam that joins crew
51
+ # to the backlog the loop oracle (`svamp issue pending`) reads.
52
+ # Pass it whenever the feature came from an issue, so the verdict lands on the
53
+ # item you are already tracking rather than creating a second one to reconcile.
54
+ # NOT passing it does NOT make the child invisible: `crew start` auto-creates a
55
+ # tracking issue at status `in_progress`, and `isPendingIssue` counts anything
56
+ # that is neither `archived` nor `paused` — so an unlinked child still holds the
57
+ # loop oracle RED until its issue is resolved. Do not treat an unlinked child as
58
+ # fire-and-forget; a lead waiting for `svamp issue pending` to empty will wait
59
+ # for it.
32
60
  # --max N bounds the child's loop iterations (a hard stop, unlike the soft
33
61
  # progress-based cap); --model <id> picks the child's model.
34
62
  svamp feature start "<brief>" --no-worktree --dir <folder>
@@ -65,14 +93,27 @@ You coordinate the project. Keep your own working tree (the base branch, e.g. `m
65
93
  branch into the base, removes the worktree, and archives the child. On a conflict or a dirty
66
94
  worktree the merge is **deferred to rework** (the child is re-woken with guidance and left
67
95
  alive — un-merged work is never destroyed); help it resolve, then merge again.
96
+ - **A merge can also HARD-fail.** When the failure is not something the child can fix by editing
97
+ (e.g. the base tree is on the wrong branch or is dirty), `feature merge` exits 1 and sends the
98
+ child `<crew-blocked>` instead of `<crew-rework>`. The child is told to STOP and hold; it will
99
+ not retry. Resolving it is **yours or the operator's** — fix the blocker in the base tree, then
100
+ re-run `svamp feature merge <child-id>`. A blocked child left unattended is idle, not working,
101
+ so do not wait for it to report again.
68
102
  - **Nudge stalled children.** If `feature list` shows a child idle with committed, ahead-of-base
69
103
  work that hasn't been reported done, send `svamp session inbox send <child> "status?"` (or
70
104
  `feature report`-style ping) to move it along.
71
105
 
72
106
  ## If you are a FEATURE CHILD
73
107
 
74
- You own one feature in an isolated worktree on your own branch, reporting to your lead. Your
75
- brief (and your loop's "until" criteria) is injected each turn.
108
+ You own one feature in an isolated worktree on your own branch, reporting to your lead.
109
+
110
+ **Your brief arrives ONCE, as the first message of this session — it is not repeated (#1607).**
111
+ Nothing re-injects it, so keeping it in view is your job: re-read it before you decide you are done,
112
+ and restate it in your own words in `feature report` notes so drift is visible to your lead. (If your
113
+ lead started you *with* `--oracle`, the native `/goal` gate does restate the "until" criteria at each
114
+ turn-end — but on the default no-oracle path there is no loop at all. See the gate table above.)
115
+ This line previously promised per-turn injection of both; no such mechanism was ever built, and
116
+ `docs/crew-design.md` §7 already recorded those hooks as never shipped.
76
117
 
77
118
  - **Work in your worktree.** Implement the feature. Commit as you go. Keep the oracle/tests green.
78
119
  - **Report.** Use `svamp feature report "<note>"` for milestones and `--blocker` when stuck —
@@ -81,8 +122,16 @@ brief (and your loop's "until" criteria) is injected each turn.
81
122
  run `svamp feature done --summary "<what changed>"`. This sends a merge-request to your lead.
82
123
  - **Do not merge your own branch or remove your own worktree** — the lead does the merge-back so
83
124
  the base tree isn't corrupted. After the lead merges, your worktree is removed and you are
84
- archived. If you get a `<crew-freeze>` message, stop editing immediately. If you get a
85
- `<crew-rework>` message, address the guidance and run `svamp feature done` again.
125
+ archived. Three control messages can arrive from your lead they look similar and mean
126
+ different things, so read the tag:
127
+ - `<crew-freeze>` — stop editing immediately.
128
+ - `<crew-rework>` — the merge was **deferred**; address the guidance and run
129
+ `svamp feature done` again.
130
+ - `<crew-blocked>` — the merge **hard-failed**. "Stop editing and hold — do not keep working.
131
+ Once the blocker is resolved, the lead can retry `svamp feature merge`." Do **not** run
132
+ `svamp feature done` again: nothing you can do from here changes the outcome, and re-sending
133
+ a merge-request into a hard-failed state just burns loop turns toward the cost ceiling. Hold
134
+ until the lead retries the merge. Your un-merged work is preserved.
86
135
 
87
136
  ## Standalone (no-worktree) children — managing a fleet of folders
88
137
 
@@ -0,0 +1,47 @@
1
+ const DEFAULT_TIMEOUTS = {
2
+ init: 6e4,
3
+ toolCall: 12e4,
4
+ think: 3e4
5
+ };
6
+ class DefaultTransport {
7
+ agentName;
8
+ constructor(agentName = "generic-acp") {
9
+ this.agentName = agentName;
10
+ }
11
+ getInitTimeout() {
12
+ return DEFAULT_TIMEOUTS.init;
13
+ }
14
+ filterStdoutLine(line) {
15
+ const trimmed = line.trim();
16
+ if (!trimmed) return null;
17
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return null;
18
+ try {
19
+ const parsed = JSON.parse(trimmed);
20
+ if (typeof parsed !== "object" || parsed === null) return null;
21
+ return line;
22
+ } catch {
23
+ return null;
24
+ }
25
+ }
26
+ handleStderr(_text, _context) {
27
+ return { message: null };
28
+ }
29
+ getToolPatterns() {
30
+ return [];
31
+ }
32
+ isInvestigationTool(_toolCallId, _toolKind) {
33
+ return false;
34
+ }
35
+ getToolCallTimeout(_toolCallId, toolKind) {
36
+ if (toolKind === "think") return DEFAULT_TIMEOUTS.think;
37
+ return DEFAULT_TIMEOUTS.toolCall;
38
+ }
39
+ extractToolNameFromId(_toolCallId) {
40
+ return null;
41
+ }
42
+ determineToolName(toolName, _toolCallId, _input, _context) {
43
+ return toolName;
44
+ }
45
+ }
46
+
47
+ export { DefaultTransport };
@@ -0,0 +1,130 @@
1
+ const GEMINI_TIMEOUTS = {
2
+ init: 12e4,
3
+ toolCall: 12e4,
4
+ investigation: 6e5,
5
+ think: 3e4,
6
+ idle: 500
7
+ };
8
+ const GEMINI_TOOL_PATTERNS = [
9
+ {
10
+ name: "change_title",
11
+ patterns: ["change_title", "change-title", "svamp__change_title", "mcp__svamp__change_title"],
12
+ inputFields: ["title"],
13
+ emptyInputDefault: true
14
+ },
15
+ {
16
+ name: "set_session_link",
17
+ patterns: ["set_session_link", "set-session-link", "svamp__set_session_link", "mcp__svamp__set_session_link"],
18
+ inputFields: ["url"]
19
+ },
20
+ {
21
+ name: "save_memory",
22
+ patterns: ["save_memory", "save-memory"],
23
+ inputFields: ["memory", "content"]
24
+ },
25
+ {
26
+ name: "think",
27
+ patterns: ["think"],
28
+ inputFields: ["thought", "thinking"]
29
+ }
30
+ ];
31
+ const AVAILABLE_MODELS = [
32
+ "gemini-2.5-pro",
33
+ "gemini-2.5-flash",
34
+ "gemini-2.5-flash-lite"
35
+ ];
36
+ class GeminiTransport {
37
+ agentName = "gemini";
38
+ getInitTimeout() {
39
+ return GEMINI_TIMEOUTS.init;
40
+ }
41
+ filterStdoutLine(line) {
42
+ const trimmed = line.trim();
43
+ if (!trimmed) return null;
44
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return null;
45
+ try {
46
+ const parsed = JSON.parse(trimmed);
47
+ if (typeof parsed !== "object" || parsed === null) return null;
48
+ return line;
49
+ } catch {
50
+ return null;
51
+ }
52
+ }
53
+ handleStderr(text, context) {
54
+ const trimmed = text.trim();
55
+ if (!trimmed) return { message: null, suppress: true };
56
+ if (trimmed.includes("status 429") || trimmed.includes('code":429') || trimmed.includes("rateLimitExceeded") || trimmed.includes("RESOURCE_EXHAUSTED")) {
57
+ return { message: null, suppress: false };
58
+ }
59
+ if (trimmed.includes("status 404") || trimmed.includes('code":404')) {
60
+ const msg = {
61
+ type: "status",
62
+ status: "error",
63
+ detail: `Model not found. Available models: ${AVAILABLE_MODELS.join(", ")}`
64
+ };
65
+ return { message: msg };
66
+ }
67
+ if (context.hasActiveInvestigation) {
68
+ const hasError = trimmed.includes("timeout") || trimmed.includes("Timeout") || trimmed.includes("failed") || trimmed.includes("Failed") || trimmed.includes("error") || trimmed.includes("Error");
69
+ if (hasError) {
70
+ return { message: null, suppress: false };
71
+ }
72
+ }
73
+ return { message: null };
74
+ }
75
+ getToolPatterns() {
76
+ return GEMINI_TOOL_PATTERNS;
77
+ }
78
+ isInvestigationTool(toolCallId, toolKind) {
79
+ const lowerId = toolCallId.toLowerCase();
80
+ return lowerId.includes("codebase_investigator") || lowerId.includes("investigator") || typeof toolKind === "string" && toolKind.includes("investigator");
81
+ }
82
+ getToolCallTimeout(toolCallId, toolKind) {
83
+ if (this.isInvestigationTool(toolCallId, toolKind)) return GEMINI_TIMEOUTS.investigation;
84
+ if (toolKind === "think") return GEMINI_TIMEOUTS.think;
85
+ return GEMINI_TIMEOUTS.toolCall;
86
+ }
87
+ getIdleTimeout() {
88
+ return GEMINI_TIMEOUTS.idle;
89
+ }
90
+ extractToolNameFromId(toolCallId) {
91
+ const lowerId = toolCallId.toLowerCase();
92
+ for (const toolPattern of GEMINI_TOOL_PATTERNS) {
93
+ for (const pattern of toolPattern.patterns) {
94
+ if (lowerId.includes(pattern.toLowerCase())) {
95
+ return toolPattern.name;
96
+ }
97
+ }
98
+ }
99
+ return null;
100
+ }
101
+ isEmptyInput(input) {
102
+ if (!input) return true;
103
+ if (Array.isArray(input)) return input.length === 0;
104
+ if (typeof input === "object") return Object.keys(input).length === 0;
105
+ return false;
106
+ }
107
+ determineToolName(toolName, toolCallId, input, _context) {
108
+ if (toolName !== "other" && toolName !== "Unknown tool") return toolName;
109
+ const idToolName = this.extractToolNameFromId(toolCallId);
110
+ if (idToolName) return idToolName;
111
+ if (input && typeof input === "object" && !Array.isArray(input)) {
112
+ const inputKeys = Object.keys(input);
113
+ for (const toolPattern of GEMINI_TOOL_PATTERNS) {
114
+ if (toolPattern.inputFields) {
115
+ const hasMatchingField = toolPattern.inputFields.some(
116
+ (field) => inputKeys.some((key) => key.toLowerCase() === field.toLowerCase())
117
+ );
118
+ if (hasMatchingField) return toolPattern.name;
119
+ }
120
+ }
121
+ }
122
+ if (this.isEmptyInput(input) && toolName === "other") {
123
+ const defaultTool = GEMINI_TOOL_PATTERNS.find((p) => p.emptyInputDefault);
124
+ if (defaultTool) return defaultTool.name;
125
+ }
126
+ return toolName;
127
+ }
128
+ }
129
+
130
+ export { GEMINI_TIMEOUTS, GeminiTransport };
@@ -0,0 +1,141 @@
1
+ const KIMI_TIMEOUTS = {
2
+ init: 12e4,
3
+ toolCall: 12e4,
4
+ investigation: 6e5,
5
+ think: 3e4,
6
+ idle: 500
7
+ };
8
+ const KIMI_TOOL_PATTERNS = [
9
+ {
10
+ name: "change_title",
11
+ patterns: ["change_title", "change-title", "svamp__change_title", "mcp__svamp__change_title"],
12
+ inputFields: ["title"],
13
+ emptyInputDefault: true
14
+ },
15
+ {
16
+ name: "set_session_link",
17
+ patterns: ["set_session_link", "set-session-link", "svamp__set_session_link", "mcp__svamp__set_session_link"],
18
+ inputFields: ["url"]
19
+ },
20
+ {
21
+ name: "save_memory",
22
+ patterns: ["save_memory", "save-memory"],
23
+ inputFields: ["memory", "content"]
24
+ },
25
+ {
26
+ name: "think",
27
+ patterns: ["think"],
28
+ inputFields: ["thought", "thinking"]
29
+ }
30
+ ];
31
+ const AVAILABLE_MODELS = [
32
+ "moonshotai/kimi-k2.5",
33
+ "moonshotai/kimi-k3",
34
+ "moonshotai/kimi-k2.7-code",
35
+ "moonshotai/kimi-k2-thinking",
36
+ "moonshotai/kimi-k2-turbo-preview",
37
+ "kimi-for-coding/kimi-for-coding"
38
+ ];
39
+ class KimiTransport {
40
+ agentName = "kimi";
41
+ getInitTimeout() {
42
+ return KIMI_TIMEOUTS.init;
43
+ }
44
+ filterStdoutLine(line) {
45
+ const trimmed = line.trim();
46
+ if (!trimmed) return null;
47
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return null;
48
+ try {
49
+ const parsed = JSON.parse(trimmed);
50
+ if (typeof parsed !== "object" || parsed === null) return null;
51
+ return line;
52
+ } catch {
53
+ return null;
54
+ }
55
+ }
56
+ handleStderr(text, context) {
57
+ const trimmed = text.trim();
58
+ if (!trimmed) return { message: null, suppress: true };
59
+ if (trimmed.includes("no provider configured") || trimmed.includes("Authentication required") || trimmed.includes("Invalid Authentication") || trimmed.includes("authentication_error") || trimmed.includes("401")) {
60
+ const msg = {
61
+ type: "status",
62
+ status: "error",
63
+ detail: "Kimi has no valid provider configured. Run `svamp daemon kimi-auth use-moonshot <api-key>` (or `use-coding` / `set`) on this machine, then restart the daemon."
64
+ };
65
+ return { message: msg };
66
+ }
67
+ if (trimmed.includes("status 429") || trimmed.includes('code":429') || trimmed.includes("429") || trimmed.includes("rate limit") || trimmed.includes("RESOURCE_EXHAUSTED")) {
68
+ return { message: null, suppress: false };
69
+ }
70
+ if (trimmed.includes("status 404") || trimmed.includes('code":404') || trimmed.includes("model_not_found")) {
71
+ const msg = {
72
+ type: "status",
73
+ status: "error",
74
+ detail: `Model not found. Example Kimi models: ${AVAILABLE_MODELS.join(", ")}`
75
+ };
76
+ return { message: msg };
77
+ }
78
+ if (context.hasActiveInvestigation) {
79
+ const hasError = trimmed.includes("timeout") || trimmed.includes("Timeout") || trimmed.includes("failed") || trimmed.includes("Failed") || trimmed.includes("error") || trimmed.includes("Error");
80
+ if (hasError) {
81
+ return { message: null, suppress: false };
82
+ }
83
+ }
84
+ return { message: null };
85
+ }
86
+ getToolPatterns() {
87
+ return KIMI_TOOL_PATTERNS;
88
+ }
89
+ isInvestigationTool(toolCallId, toolKind) {
90
+ const lowerId = toolCallId.toLowerCase();
91
+ return lowerId.includes("codebase_investigator") || lowerId.includes("investigator") || typeof toolKind === "string" && toolKind.includes("investigator");
92
+ }
93
+ getToolCallTimeout(toolCallId, toolKind) {
94
+ if (this.isInvestigationTool(toolCallId, toolKind)) return KIMI_TIMEOUTS.investigation;
95
+ if (toolKind === "think") return KIMI_TIMEOUTS.think;
96
+ return KIMI_TIMEOUTS.toolCall;
97
+ }
98
+ getIdleTimeout() {
99
+ return KIMI_TIMEOUTS.idle;
100
+ }
101
+ extractToolNameFromId(toolCallId) {
102
+ const lowerId = toolCallId.toLowerCase();
103
+ for (const toolPattern of KIMI_TOOL_PATTERNS) {
104
+ for (const pattern of toolPattern.patterns) {
105
+ if (lowerId.includes(pattern.toLowerCase())) {
106
+ return toolPattern.name;
107
+ }
108
+ }
109
+ }
110
+ return null;
111
+ }
112
+ isEmptyInput(input) {
113
+ if (!input) return true;
114
+ if (Array.isArray(input)) return input.length === 0;
115
+ if (typeof input === "object") return Object.keys(input).length === 0;
116
+ return false;
117
+ }
118
+ determineToolName(toolName, toolCallId, input, _context) {
119
+ if (toolName !== "other" && toolName !== "Unknown tool") return toolName;
120
+ const idToolName = this.extractToolNameFromId(toolCallId);
121
+ if (idToolName) return idToolName;
122
+ if (input && typeof input === "object" && !Array.isArray(input)) {
123
+ const inputKeys = Object.keys(input);
124
+ for (const toolPattern of KIMI_TOOL_PATTERNS) {
125
+ if (toolPattern.inputFields) {
126
+ const hasMatchingField = toolPattern.inputFields.some(
127
+ (field) => inputKeys.some((key) => key.toLowerCase() === field.toLowerCase())
128
+ );
129
+ if (hasMatchingField) return toolPattern.name;
130
+ }
131
+ }
132
+ }
133
+ if (this.isEmptyInput(input) && toolName === "other") {
134
+ const defaultTool = KIMI_TOOL_PATTERNS.find((p) => p.emptyInputDefault);
135
+ if (defaultTool) return defaultTool.name;
136
+ }
137
+ return toolName;
138
+ }
139
+ }
140
+
141
+ export { KIMI_TIMEOUTS, KimiTransport };
@@ -0,0 +1,48 @@
1
+ const KNOWN_ACP_AGENTS = {
2
+ gemini: { command: "gemini", args: ["--experimental-acp"] },
3
+ kimi: { command: "kimi", args: ["acp"] }
4
+ };
5
+ const KNOWN_CODEX_AGENTS = {
6
+ codex: { command: "codex", args: ["app-server", "--listen", "stdio://"] }
7
+ };
8
+ function resolveAcpAgentConfig(cliArgs) {
9
+ if (cliArgs.length === 0) {
10
+ throw new Error("Usage: svamp agent <agent-name> or svamp agent -- <command> [args]");
11
+ }
12
+ if (cliArgs[0] === "--") {
13
+ const command = cliArgs[1];
14
+ if (!command) {
15
+ throw new Error('Missing command after "--". Usage: svamp agent -- <command> [args]');
16
+ }
17
+ return {
18
+ agentName: command,
19
+ command,
20
+ args: cliArgs.slice(2)
21
+ };
22
+ }
23
+ const agentName = cliArgs[0];
24
+ const knownAcp = KNOWN_ACP_AGENTS[agentName];
25
+ if (knownAcp) {
26
+ const passthroughArgs = cliArgs.slice(1).filter(Boolean);
27
+ return {
28
+ agentName,
29
+ command: knownAcp.command,
30
+ args: [...knownAcp.args, ...passthroughArgs]
31
+ };
32
+ }
33
+ const knownCodex = KNOWN_CODEX_AGENTS[agentName];
34
+ if (knownCodex) {
35
+ return {
36
+ agentName,
37
+ command: knownCodex.command,
38
+ args: [...knownCodex.args, ...cliArgs.slice(1)]
39
+ };
40
+ }
41
+ return {
42
+ agentName,
43
+ command: agentName,
44
+ args: cliArgs.slice(1)
45
+ };
46
+ }
47
+
48
+ export { KNOWN_ACP_AGENTS, KNOWN_CODEX_AGENTS, resolveAcpAgentConfig };