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.
- package/bin/skills/artifact/SKILL.md +19 -5
- package/bin/skills/crew/SKILL.md +62 -13
- package/dist/DefaultTransport-vfp78fug.mjs +47 -0
- package/dist/GeminiTransport-3OkhtFRO.mjs +130 -0
- package/dist/KimiTransport-D8n009Tr.mjs +141 -0
- package/dist/acpAgentConfig-CObOPlA8.mjs +48 -0
- package/dist/acpBackend-UaBD4zL3.mjs +925 -0
- package/dist/{adminCommands-CRTBDZan.mjs → adminCommands-BvsYnRDA.mjs} +2 -18
- package/dist/{agentCommands-BuO988tJ.mjs → agentCommands-D91RcjqQ.mjs} +6 -23
- package/dist/api-BHjyC0Ph.mjs +157 -0
- package/dist/{auth--WS0wezs.mjs → auth-CRz8TsEl.mjs} +1 -22
- package/dist/bootStatus-DqtNgJch.mjs +143 -0
- package/dist/claudeAuth-C0wc1GTM.mjs +291 -0
- package/dist/cli.mjs +2948 -5
- package/dist/codexAppServerBackend-VGsYtbqg.mjs +786 -0
- package/dist/codexProvider-BtxYc5Rj.mjs +68 -0
- package/dist/{commands-DDuGBSsz.mjs → commands-B-yczgz_.mjs} +115 -49
- package/dist/{commands-DU4B-4Gh.mjs → commands-BMeDrpSv.mjs} +55 -25
- package/dist/{commands-DXLOKJOj.mjs → commands-BbmypSgx.mjs} +62 -27
- package/dist/{commands-C0HQiu-S.mjs → commands-BkdbelFo.mjs} +78 -38
- package/dist/{commands-DXM17M6n.mjs → commands-C13-yE2R.mjs} +42 -19
- package/dist/{commands-BKb1g8Gq.mjs → commands-CoOc4qiw.mjs} +20 -30
- package/dist/{commands-BsKgZ5sQ.mjs → commands-DW5RnHZw.mjs} +2 -19
- package/dist/{commands-Cwxy-C5N.mjs → commands-sPvRcG3J.mjs} +10 -32
- package/dist/cron-hHGdb5kU.mjs +132 -0
- package/dist/detectIsolation-CrV8IFDv.mjs +339 -0
- package/dist/envFlag-COOuJ3AH.mjs +11 -0
- package/dist/fileLock-BMPwCrB6.mjs +80 -0
- package/dist/{fleet-BHzlAvnu.mjs → fleet-Ca-UjJNL.mjs} +122 -36
- package/dist/flushExit-C-i6SGjj.mjs +37 -0
- package/dist/friendlyName-1UZcyDhu.mjs +586 -0
- package/dist/{headlessCli-C2gvOpOt.mjs → headlessCli-C8HzQvis.mjs} +3 -21
- package/dist/hookSettings-FFc1pi8j.mjs +10546 -0
- package/dist/{httpServer-Be1Hcddr.mjs → httpServer-BzEDbOGn.mjs} +14 -5
- package/dist/hyphaClient-ybivOjZo.mjs +49 -0
- package/dist/index.mjs +40 -8
- package/dist/instance-CeHz5C8v.mjs +203 -0
- package/dist/kimiProvider-DbxODebR.mjs +69 -0
- package/dist/kimiSetup-NGsoYB5h.mjs +48 -0
- package/dist/loopVerify-DALtTk2V.mjs +444 -0
- package/dist/machineTools-DygM1jXW.mjs +580 -0
- package/dist/model-CUHojegn.mjs +66 -0
- package/dist/{notifyCommands-0aZUwKQK.mjs → notifyCommands-CCUE18Q-.mjs} +3 -18
- package/dist/package-BrsiNutS.mjs +64 -0
- package/dist/pinnedCodex-XW4qvOAe.mjs +11 -0
- package/dist/pinnedNono-5ZHb6328.mjs +15 -0
- package/dist/{rpc-rAQQ1q8t.mjs → rpc-CBkSkzsI.mjs} +38 -6
- package/dist/{rpc-B2Lc2BAB.mjs → rpc-ccY7xvlo.mjs} +33 -22
- package/dist/{run-C7QVfyw4.mjs → run-DCq3aEiF.mjs} +17 -9
- package/dist/run-DDmeV9K3.mjs +9826 -0
- package/dist/sandboxDetect-Cdzsj5TC.mjs +19 -0
- package/dist/{scheduler-CzF4lOe_.mjs → scheduler-BkVJ27Ki.mjs} +38 -6
- package/dist/securityContext-Cm5UTCuD.mjs +218 -0
- package/dist/serveAuth-4M0vwMyj.mjs +460 -0
- package/dist/{serveCommands-EIEqdaVu.mjs → serveCommands-CLhkccJM.mjs} +13 -34
- package/dist/sharingNotificationSync-qGIQ-3yf.mjs +359 -0
- package/dist/{sideband-BoutiRJi.mjs → sideband-68HhGT0i.mjs} +1 -21
- package/dist/store-B4E6NzG6.mjs +356 -0
- package/dist/store-fi0f-ff1.mjs +184 -0
- package/dist/types-57J3Re0n.mjs +17 -0
- package/dist/wantsHelp-6bMOeNUz.mjs +5 -0
- package/dist/wrapCommand-CdNawSaa.mjs +117 -0
- package/package.json +3 -3
- package/dist/cli-B24CmIli.mjs +0 -2780
- package/dist/package-CTbGYaJG.mjs +0 -64
- package/dist/run-CF4kAVPW.mjs +0 -26508
- package/dist/sandboxDetect-DNTcbgWD.mjs +0 -12
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: artifact
|
|
3
|
-
version: 1.3.
|
|
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
|
-
#
|
|
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
|
-
#
|
|
141
|
-
svamp serve dashboard ./outputs --
|
|
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
|
package/bin/skills/crew/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: crew
|
|
3
|
-
version: 0.3.
|
|
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
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
# --
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
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.
|
|
75
|
-
|
|
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.
|
|
85
|
-
|
|
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 };
|