@alpacakit/agents 0.1.0-beta.19

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 (249) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +224 -0
  3. package/dist/agents/builtin.d.ts +19 -0
  4. package/dist/agents/builtin.d.ts.map +1 -0
  5. package/dist/agents/builtin.js +14 -0
  6. package/dist/agents/claude.d.ts +20 -0
  7. package/dist/agents/claude.d.ts.map +1 -0
  8. package/dist/agents/claude.js +480 -0
  9. package/dist/agents/codex.d.ts +19 -0
  10. package/dist/agents/codex.d.ts.map +1 -0
  11. package/dist/agents/codex.js +355 -0
  12. package/dist/agents/openai-compatible/config.d.ts +16 -0
  13. package/dist/agents/openai-compatible/config.d.ts.map +1 -0
  14. package/dist/agents/openai-compatible/config.js +160 -0
  15. package/dist/agents/openai-compatible/engine.d.ts +18 -0
  16. package/dist/agents/openai-compatible/engine.d.ts.map +1 -0
  17. package/dist/agents/openai-compatible/engine.js +10 -0
  18. package/dist/agents/openai-compatible/fetch-engine.d.ts +38 -0
  19. package/dist/agents/openai-compatible/fetch-engine.d.ts.map +1 -0
  20. package/dist/agents/openai-compatible/fetch-engine.js +216 -0
  21. package/dist/agents/openai-compatible/pi/engine.d.ts +32 -0
  22. package/dist/agents/openai-compatible/pi/engine.d.ts.map +1 -0
  23. package/dist/agents/openai-compatible/pi/engine.js +98 -0
  24. package/dist/agents/openai-compatible/pi/module.d.ts +87 -0
  25. package/dist/agents/openai-compatible/pi/module.d.ts.map +1 -0
  26. package/dist/agents/openai-compatible/pi/module.js +43 -0
  27. package/dist/agents/openai-compatible/pi/sandbox.d.ts +25 -0
  28. package/dist/agents/openai-compatible/pi/sandbox.d.ts.map +1 -0
  29. package/dist/agents/openai-compatible/pi/sandbox.js +69 -0
  30. package/dist/agents/openai-compatible/pi/session.d.ts +17 -0
  31. package/dist/agents/openai-compatible/pi/session.d.ts.map +1 -0
  32. package/dist/agents/openai-compatible/pi/session.js +354 -0
  33. package/dist/agents/openai-compatible/response.d.ts +19 -0
  34. package/dist/agents/openai-compatible/response.d.ts.map +1 -0
  35. package/dist/agents/openai-compatible/response.js +203 -0
  36. package/dist/agents/openai-compatible/types.d.ts +34 -0
  37. package/dist/agents/openai-compatible/types.d.ts.map +1 -0
  38. package/dist/agents/openai-compatible/types.js +1 -0
  39. package/dist/agents/openai-compatible.d.ts +12 -0
  40. package/dist/agents/openai-compatible.d.ts.map +1 -0
  41. package/dist/agents/openai-compatible.js +45 -0
  42. package/dist/auth/login-probe.d.ts +21 -0
  43. package/dist/auth/login-probe.d.ts.map +1 -0
  44. package/dist/auth/login-probe.js +47 -0
  45. package/dist/auth/login.d.ts +79 -0
  46. package/dist/auth/login.d.ts.map +1 -0
  47. package/dist/auth/login.js +9 -0
  48. package/dist/auth/material.d.ts +87 -0
  49. package/dist/auth/material.d.ts.map +1 -0
  50. package/dist/auth/material.js +59 -0
  51. package/dist/auth/profile-store.d.ts +14 -0
  52. package/dist/auth/profile-store.d.ts.map +1 -0
  53. package/dist/auth/profile-store.js +101 -0
  54. package/dist/auth/secrets.d.ts +14 -0
  55. package/dist/auth/secrets.d.ts.map +1 -0
  56. package/dist/auth/secrets.js +40 -0
  57. package/dist/codex/app-server.d.ts +30 -0
  58. package/dist/codex/app-server.d.ts.map +1 -0
  59. package/dist/codex/app-server.js +322 -0
  60. package/dist/codex/index.d.ts +3 -0
  61. package/dist/codex/index.d.ts.map +1 -0
  62. package/dist/codex/index.js +1 -0
  63. package/dist/codex/protocol.d.ts +124 -0
  64. package/dist/codex/protocol.d.ts.map +1 -0
  65. package/dist/codex/protocol.js +283 -0
  66. package/dist/config/agent-config-validation.d.ts +22 -0
  67. package/dist/config/agent-config-validation.d.ts.map +1 -0
  68. package/dist/config/agent-config-validation.js +31 -0
  69. package/dist/config/agent-configs.d.ts +37 -0
  70. package/dist/config/agent-configs.d.ts.map +1 -0
  71. package/dist/config/agent-configs.js +214 -0
  72. package/dist/config/compose.d.ts +10 -0
  73. package/dist/config/compose.d.ts.map +1 -0
  74. package/dist/config/compose.js +89 -0
  75. package/dist/config/config.d.ts +21 -0
  76. package/dist/config/config.d.ts.map +1 -0
  77. package/dist/config/config.js +33 -0
  78. package/dist/config/errors.d.ts +45 -0
  79. package/dist/config/errors.d.ts.map +1 -0
  80. package/dist/config/errors.js +37 -0
  81. package/dist/config/extension.d.ts +3 -0
  82. package/dist/config/extension.d.ts.map +1 -0
  83. package/dist/config/extension.js +1 -0
  84. package/dist/config/ids.d.ts +8 -0
  85. package/dist/config/ids.d.ts.map +1 -0
  86. package/dist/config/ids.js +4 -0
  87. package/dist/config/index.d.ts +12 -0
  88. package/dist/config/index.d.ts.map +1 -0
  89. package/dist/config/index.js +6 -0
  90. package/dist/config/model.d.ts +105 -0
  91. package/dist/config/model.d.ts.map +1 -0
  92. package/dist/config/model.js +1 -0
  93. package/dist/config/paths.d.ts +7 -0
  94. package/dist/config/paths.d.ts.map +1 -0
  95. package/dist/config/paths.js +10 -0
  96. package/dist/config/schema.d.ts +59 -0
  97. package/dist/config/schema.d.ts.map +1 -0
  98. package/dist/config/schema.js +83 -0
  99. package/dist/config/store.d.ts +34 -0
  100. package/dist/config/store.d.ts.map +1 -0
  101. package/dist/config/store.js +420 -0
  102. package/dist/config/support.d.ts +14 -0
  103. package/dist/config/support.d.ts.map +1 -0
  104. package/dist/config/support.js +32 -0
  105. package/dist/config/values.d.ts +21 -0
  106. package/dist/config/values.d.ts.map +1 -0
  107. package/dist/config/values.js +22 -0
  108. package/dist/config/xdg.d.ts +20 -0
  109. package/dist/config/xdg.d.ts.map +1 -0
  110. package/dist/config/xdg.js +23 -0
  111. package/dist/core/async-queue.d.ts +14 -0
  112. package/dist/core/async-queue.d.ts.map +1 -0
  113. package/dist/core/async-queue.js +46 -0
  114. package/dist/core/env.d.ts +19 -0
  115. package/dist/core/env.d.ts.map +1 -0
  116. package/dist/core/env.js +39 -0
  117. package/dist/core/error.d.ts +121 -0
  118. package/dist/core/error.d.ts.map +1 -0
  119. package/dist/core/error.js +88 -0
  120. package/dist/core/fs.d.ts +16 -0
  121. package/dist/core/fs.d.ts.map +1 -0
  122. package/dist/core/fs.js +30 -0
  123. package/dist/core/json.d.ts +20 -0
  124. package/dist/core/json.d.ts.map +1 -0
  125. package/dist/core/json.js +42 -0
  126. package/dist/core/node-ports.d.ts +9 -0
  127. package/dist/core/node-ports.d.ts.map +1 -0
  128. package/dist/core/node-ports.js +135 -0
  129. package/dist/core/ports.d.ts +71 -0
  130. package/dist/core/ports.d.ts.map +1 -0
  131. package/dist/core/ports.js +19 -0
  132. package/dist/core/redact.d.ts +8 -0
  133. package/dist/core/redact.d.ts.map +1 -0
  134. package/dist/core/redact.js +66 -0
  135. package/dist/core/run.d.ts +125 -0
  136. package/dist/core/run.d.ts.map +1 -0
  137. package/dist/core/run.js +27 -0
  138. package/dist/core/values.d.ts +12 -0
  139. package/dist/core/values.d.ts.map +1 -0
  140. package/dist/core/values.js +9 -0
  141. package/dist/corpus/failure-corpus.d.ts +21 -0
  142. package/dist/corpus/failure-corpus.d.ts.map +1 -0
  143. package/dist/corpus/failure-corpus.js +89 -0
  144. package/dist/fleet/fleet.d.ts +120 -0
  145. package/dist/fleet/fleet.d.ts.map +1 -0
  146. package/dist/fleet/fleet.js +409 -0
  147. package/dist/fleet/policy.d.ts +45 -0
  148. package/dist/fleet/policy.d.ts.map +1 -0
  149. package/dist/fleet/policy.js +50 -0
  150. package/dist/fleet/store.d.ts +28 -0
  151. package/dist/fleet/store.d.ts.map +1 -0
  152. package/dist/fleet/store.js +225 -0
  153. package/dist/host/capabilities.d.ts +146 -0
  154. package/dist/host/capabilities.d.ts.map +1 -0
  155. package/dist/host/capabilities.js +59 -0
  156. package/dist/host/context.d.ts +29 -0
  157. package/dist/host/context.d.ts.map +1 -0
  158. package/dist/host/context.js +11 -0
  159. package/dist/host/github-dir.d.ts +16 -0
  160. package/dist/host/github-dir.d.ts.map +1 -0
  161. package/dist/host/github-dir.js +164 -0
  162. package/dist/host/mcp-list.d.ts +12 -0
  163. package/dist/host/mcp-list.d.ts.map +1 -0
  164. package/dist/host/mcp-list.js +122 -0
  165. package/dist/host/requirement-ref.d.ts +12 -0
  166. package/dist/host/requirement-ref.d.ts.map +1 -0
  167. package/dist/host/requirement-ref.js +40 -0
  168. package/dist/host/run-command.d.ts +27 -0
  169. package/dist/host/run-command.d.ts.map +1 -0
  170. package/dist/host/run-command.js +66 -0
  171. package/dist/host/validate-spec.d.ts +10 -0
  172. package/dist/host/validate-spec.d.ts.map +1 -0
  173. package/dist/host/validate-spec.js +69 -0
  174. package/dist/index.d.ts +69 -0
  175. package/dist/index.d.ts.map +1 -0
  176. package/dist/index.js +41 -0
  177. package/dist/kernel/classify.d.ts +44 -0
  178. package/dist/kernel/classify.d.ts.map +1 -0
  179. package/dist/kernel/classify.js +136 -0
  180. package/dist/kernel/cli-agent.d.ts +62 -0
  181. package/dist/kernel/cli-agent.d.ts.map +1 -0
  182. package/dist/kernel/cli-agent.js +281 -0
  183. package/dist/kernel/cli-login.d.ts +13 -0
  184. package/dist/kernel/cli-login.d.ts.map +1 -0
  185. package/dist/kernel/cli-login.js +199 -0
  186. package/dist/kernel/definition.d.ts +41 -0
  187. package/dist/kernel/definition.d.ts.map +1 -0
  188. package/dist/kernel/definition.js +13 -0
  189. package/dist/kernel/env-template.d.ts +17 -0
  190. package/dist/kernel/env-template.d.ts.map +1 -0
  191. package/dist/kernel/env-template.js +78 -0
  192. package/dist/kernel/facets.d.ts +58 -0
  193. package/dist/kernel/facets.d.ts.map +1 -0
  194. package/dist/kernel/facets.js +102 -0
  195. package/dist/kernel/guard.d.ts +24 -0
  196. package/dist/kernel/guard.d.ts.map +1 -0
  197. package/dist/kernel/guard.js +69 -0
  198. package/dist/kernel/handle.d.ts +29 -0
  199. package/dist/kernel/handle.d.ts.map +1 -0
  200. package/dist/kernel/handle.js +97 -0
  201. package/dist/kernel/keyed.d.ts +19 -0
  202. package/dist/kernel/keyed.d.ts.map +1 -0
  203. package/dist/kernel/keyed.js +35 -0
  204. package/dist/kernel/login.d.ts +20 -0
  205. package/dist/kernel/login.d.ts.map +1 -0
  206. package/dist/kernel/login.js +74 -0
  207. package/dist/kernel/options.d.ts +73 -0
  208. package/dist/kernel/options.d.ts.map +1 -0
  209. package/dist/kernel/options.js +41 -0
  210. package/dist/kernel/run.d.ts +20 -0
  211. package/dist/kernel/run.d.ts.map +1 -0
  212. package/dist/kernel/run.js +245 -0
  213. package/dist/kernel/runtime.d.ts +48 -0
  214. package/dist/kernel/runtime.d.ts.map +1 -0
  215. package/dist/kernel/runtime.js +108 -0
  216. package/dist/keyring/index.d.ts +2 -0
  217. package/dist/keyring/index.d.ts.map +1 -0
  218. package/dist/keyring/index.js +1 -0
  219. package/dist/keyring/resolver.d.ts +9 -0
  220. package/dist/keyring/resolver.d.ts.map +1 -0
  221. package/dist/keyring/resolver.js +62 -0
  222. package/dist/run-agent.d.ts +19 -0
  223. package/dist/run-agent.d.ts.map +1 -0
  224. package/dist/run-agent.js +25 -0
  225. package/dist/structured/extract.d.ts +27 -0
  226. package/dist/structured/extract.d.ts.map +1 -0
  227. package/dist/structured/extract.js +188 -0
  228. package/dist/structured/run-structured.d.ts +83 -0
  229. package/dist/structured/run-structured.d.ts.map +1 -0
  230. package/dist/structured/run-structured.js +202 -0
  231. package/dist/structured/standard-schema.d.ts +34 -0
  232. package/dist/structured/standard-schema.d.ts.map +1 -0
  233. package/dist/structured/standard-schema.js +6 -0
  234. package/dist/testing/fake-agent.d.ts +32 -0
  235. package/dist/testing/fake-agent.d.ts.map +1 -0
  236. package/dist/testing/fake-agent.js +43 -0
  237. package/dist/testing/fake-clock.d.ts +8 -0
  238. package/dist/testing/fake-clock.d.ts.map +1 -0
  239. package/dist/testing/fake-clock.js +44 -0
  240. package/dist/testing/fake-exec.d.ts +26 -0
  241. package/dist/testing/fake-exec.d.ts.map +1 -0
  242. package/dist/testing/fake-exec.js +69 -0
  243. package/dist/testing/fake-secrets.d.ts +3 -0
  244. package/dist/testing/fake-secrets.d.ts.map +1 -0
  245. package/dist/testing/fake-secrets.js +5 -0
  246. package/dist/testing/index.d.ts +10 -0
  247. package/dist/testing/index.d.ts.map +1 -0
  248. package/dist/testing/index.js +9 -0
  249. package/package.json +75 -0
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Fleet state store port (design A4/A5): cooldowns keyed by candidate
3
+ * identity, sticky auth marks keyed by profile id. Expiry is purely
4
+ * time-based and evaluated by the fleet, not the store. The file
5
+ * implementation writes atomically (temp + rename) under an advisory
6
+ * cross-process lock with 0700/0600 modes. Fleet state is a cache:
7
+ * unknown versions are discarded and reconstructed from future runs.
8
+ */
9
+ import { mkdir, open, readFile, rename, rm, stat } from "node:fs/promises";
10
+ import path from "node:path";
11
+ import { AGENT_ERROR_CODE, AgentError } from "../core/error.js";
12
+ import { isErrnoCode, PRIVATE_DIRECTORY_MODE, PRIVATE_FILE_MODE, writeFileAtomically, } from "../core/fs.js";
13
+ import { systemClock } from "../core/node-ports.js";
14
+ import { TEXT_ENCODING } from "../core/values.js";
15
+ class MemoryFleetStateStore {
16
+ cooldowns = new Map();
17
+ authInvalid = new Set();
18
+ getCooldownUntil(key) {
19
+ return Promise.resolve(this.cooldowns.get(key) ?? null);
20
+ }
21
+ setCooldownUntil(key, untilIso) {
22
+ this.cooldowns.set(key, untilIso);
23
+ return Promise.resolve();
24
+ }
25
+ isAuthInvalid(profileId) {
26
+ return Promise.resolve(this.authInvalid.has(profileId));
27
+ }
28
+ markAuthInvalid(profileId) {
29
+ this.authInvalid.add(profileId);
30
+ return Promise.resolve();
31
+ }
32
+ }
33
+ const FLEET_STATE_FILE_VERSION = 1;
34
+ const EXCLUSIVE_CREATE_FLAG = "wx";
35
+ const LOCK_FILE_SUFFIX = "lock";
36
+ const STALE_LOCK_SUFFIX = "stale";
37
+ const EMPTY_STATE = {
38
+ version: FLEET_STATE_FILE_VERSION,
39
+ cooldowns: {},
40
+ authInvalid: [],
41
+ };
42
+ const LOCK_STALE_MS = 30_000;
43
+ const LOCK_RETRY_MS = 10;
44
+ const LOCK_MAX_ATTEMPTS = 300;
45
+ const sleep = (clock, ms) => new Promise((resolve) => {
46
+ clock.setTimeout(resolve, ms);
47
+ });
48
+ async function isStaleLock(lockPath, clock) {
49
+ try {
50
+ const info = await stat(lockPath);
51
+ return clock.now() - info.mtimeMs > LOCK_STALE_MS;
52
+ }
53
+ catch {
54
+ return false;
55
+ }
56
+ }
57
+ let staleTakeoverSequence = 0;
58
+ /**
59
+ * Single-winner stale-lock takeover: rename the stale lock to a name
60
+ * only this contender uses, then remove the renamed file. Rename is
61
+ * atomic, so of several waiters that observed the same stale lock
62
+ * exactly one wins; losers see ENOENT and re-enter the acquire loop
63
+ * instead of deleting whatever (possibly fresh) lock now sits there.
64
+ */
65
+ async function takeOverStaleLock(lockPath) {
66
+ staleTakeoverSequence += 1;
67
+ const stalePath = [
68
+ lockPath,
69
+ STALE_LOCK_SUFFIX,
70
+ String(process.pid),
71
+ String(staleTakeoverSequence),
72
+ ].join(".");
73
+ try {
74
+ await rename(lockPath, stalePath);
75
+ }
76
+ catch (error) {
77
+ if (isErrnoCode(error, "ENOENT")) {
78
+ // Another contender already took the stale lock over.
79
+ return;
80
+ }
81
+ throw new AgentError({
82
+ code: AGENT_ERROR_CODE.failed,
83
+ message: `Could not take over stale fleet store lock: ${lockPath}`,
84
+ data: { exitCode: null, stdoutTail: null, stderrTail: null },
85
+ cause: error,
86
+ });
87
+ }
88
+ await rm(stalePath, { force: true });
89
+ }
90
+ async function withFileLock(filePath, clock, task) {
91
+ await mkdir(path.dirname(filePath), {
92
+ recursive: true,
93
+ mode: PRIVATE_DIRECTORY_MODE,
94
+ });
95
+ const lockPath = [filePath, LOCK_FILE_SUFFIX].join(".");
96
+ let locked = false;
97
+ for (let attempt = 0; attempt < LOCK_MAX_ATTEMPTS; attempt += 1) {
98
+ try {
99
+ const handle = await open(lockPath, EXCLUSIVE_CREATE_FLAG, PRIVATE_FILE_MODE);
100
+ try {
101
+ // Record the owning pid for post-mortem debugging of stale locks.
102
+ await handle.writeFile(`${process.pid}\n`, TEXT_ENCODING);
103
+ }
104
+ finally {
105
+ await handle.close();
106
+ }
107
+ locked = true;
108
+ break;
109
+ }
110
+ catch (error) {
111
+ if (!isErrnoCode(error, "EEXIST")) {
112
+ // Not lock contention (permissions, read-only fs, ...): retrying
113
+ // cannot help, so fail immediately with the real cause.
114
+ throw new AgentError({
115
+ code: AGENT_ERROR_CODE.failed,
116
+ message: `Could not create fleet store lock: ${lockPath}`,
117
+ data: { exitCode: null, stdoutTail: null, stderrTail: null },
118
+ cause: error,
119
+ });
120
+ }
121
+ if (await isStaleLock(lockPath, clock)) {
122
+ await takeOverStaleLock(lockPath);
123
+ continue;
124
+ }
125
+ if (attempt === LOCK_MAX_ATTEMPTS - 1) {
126
+ throw new AgentError({
127
+ code: AGENT_ERROR_CODE.failed,
128
+ message: `Timed out waiting for fleet store lock: ${lockPath}`,
129
+ data: { exitCode: null, stdoutTail: null, stderrTail: null },
130
+ cause: error,
131
+ });
132
+ }
133
+ await sleep(clock, LOCK_RETRY_MS);
134
+ }
135
+ }
136
+ try {
137
+ return await task();
138
+ }
139
+ finally {
140
+ if (locked) {
141
+ await rm(lockPath, { force: true });
142
+ }
143
+ }
144
+ }
145
+ async function loadState(filePath) {
146
+ let raw;
147
+ try {
148
+ raw = await readFile(filePath, TEXT_ENCODING);
149
+ }
150
+ catch {
151
+ return structuredClone(EMPTY_STATE);
152
+ }
153
+ try {
154
+ const parsed = JSON.parse(raw);
155
+ if (typeof parsed !== "object" || parsed === null) {
156
+ return structuredClone(EMPTY_STATE);
157
+ }
158
+ const record = parsed;
159
+ if (record.version !== FLEET_STATE_FILE_VERSION) {
160
+ return structuredClone(EMPTY_STATE);
161
+ }
162
+ return {
163
+ version: FLEET_STATE_FILE_VERSION,
164
+ cooldowns: typeof record.cooldowns === "object" && record.cooldowns !== null
165
+ ? record.cooldowns
166
+ : {},
167
+ authInvalid: Array.isArray(record.authInvalid) ? record.authInvalid : [],
168
+ };
169
+ }
170
+ catch {
171
+ return structuredClone(EMPTY_STATE);
172
+ }
173
+ }
174
+ async function saveState(filePath, state) {
175
+ await writeFileAtomically(filePath, `${JSON.stringify(state, null, 2)}\n`);
176
+ }
177
+ class FileFleetStateStore {
178
+ filePath;
179
+ clock;
180
+ // Serialize mutations so concurrent fleet attempts cannot interleave
181
+ // read-modify-write cycles within this process.
182
+ queue = Promise.resolve();
183
+ constructor(filePath, clock) {
184
+ this.filePath = filePath;
185
+ this.clock = clock;
186
+ }
187
+ getCooldownUntil(key) {
188
+ return this.enqueue(async () => (await loadState(this.filePath)).cooldowns[key] ?? null);
189
+ }
190
+ setCooldownUntil(key, untilIso) {
191
+ return this.enqueue(() => withFileLock(this.filePath, this.clock, async () => {
192
+ const state = await loadState(this.filePath);
193
+ state.cooldowns[key] = untilIso;
194
+ await saveState(this.filePath, state);
195
+ }));
196
+ }
197
+ isAuthInvalid(profileId) {
198
+ return this.enqueue(async () => (await loadState(this.filePath)).authInvalid.includes(profileId));
199
+ }
200
+ markAuthInvalid(profileId) {
201
+ return this.enqueue(() => withFileLock(this.filePath, this.clock, async () => {
202
+ const state = await loadState(this.filePath);
203
+ if (!state.authInvalid.includes(profileId)) {
204
+ state.authInvalid.push(profileId);
205
+ await saveState(this.filePath, state);
206
+ }
207
+ }));
208
+ }
209
+ enqueue(task) {
210
+ const next = this.queue.then(task);
211
+ this.queue = next.catch(() => { });
212
+ return next;
213
+ }
214
+ }
215
+ export function memoryFleetStore() {
216
+ return new MemoryFleetStateStore();
217
+ }
218
+ /**
219
+ * Lock retry pacing and stale-lock age both read the injected clock
220
+ * (inbound, rule 1: absent = production system clock) so contention and
221
+ * takeover are testable without real waiting.
222
+ */
223
+ export function fileFleetStore(filePath, clock) {
224
+ return new FileFleetStateStore(filePath, clock ?? systemClock);
225
+ }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Host-plane capability contracts (design "Skills and MCP"). Keyed
3
+ * handler records per source kind / transport: presence = supported,
4
+ * absence = typed unsupported. Concrete implementations live on each
5
+ * adapter; the contracts and shared spec types live here.
6
+ */
7
+ import type { HostContext } from "./context.js";
8
+ export declare const SKILL_SOURCE_KIND: {
9
+ readonly localDir: "local-dir";
10
+ readonly githubDir: "github-dir";
11
+ readonly plugin: "plugin";
12
+ };
13
+ export type SkillSource = {
14
+ kind: typeof SKILL_SOURCE_KIND.localDir;
15
+ path: string;
16
+ } | {
17
+ kind: typeof SKILL_SOURCE_KIND.githubDir;
18
+ repo: string;
19
+ ref: string;
20
+ path: string;
21
+ } | {
22
+ kind: typeof SKILL_SOURCE_KIND.plugin;
23
+ name: string;
24
+ };
25
+ export type SkillSourceKind = SkillSource["kind"];
26
+ export type InstalledSkill = {
27
+ id: string;
28
+ name: string;
29
+ description: string;
30
+ location: string;
31
+ source: string;
32
+ };
33
+ export type InstallOptions = {
34
+ overwrite?: boolean;
35
+ };
36
+ export declare const INSTALL_STATUS: {
37
+ readonly installed: "installed";
38
+ readonly alreadyInstalled: "already-installed";
39
+ };
40
+ export type InstallOutcome = {
41
+ status: typeof INSTALL_STATUS.installed;
42
+ location: string;
43
+ } | {
44
+ status: typeof INSTALL_STATUS.alreadyInstalled;
45
+ location: string;
46
+ };
47
+ export interface SkillsCapability {
48
+ list(ctx: HostContext): Promise<InstalledSkill[]>;
49
+ install: {
50
+ [K in SkillSourceKind]?: (ctx: HostContext, source: Extract<SkillSource, {
51
+ kind: K;
52
+ }>, options: InstallOptions) => Promise<InstallOutcome>;
53
+ };
54
+ }
55
+ export declare const MCP_TRANSPORT: {
56
+ readonly stdio: "stdio";
57
+ readonly http: "http";
58
+ };
59
+ export type McpServerSpec = {
60
+ transport: typeof MCP_TRANSPORT.stdio;
61
+ command: string;
62
+ args: string[];
63
+ env: Record<string, string>;
64
+ cwd: string | null;
65
+ } | {
66
+ transport: typeof MCP_TRANSPORT.http;
67
+ url: string;
68
+ bearerTokenEnvVar: string | null;
69
+ headers: Record<string, string>;
70
+ };
71
+ export type McpTransport = McpServerSpec["transport"];
72
+ export type StdioMcpServerSpec = Extract<McpServerSpec, {
73
+ transport: typeof MCP_TRANSPORT.stdio;
74
+ }>;
75
+ export declare const MCP_SCOPE: {
76
+ readonly project: "project";
77
+ readonly user: "user";
78
+ };
79
+ export type McpScope = (typeof MCP_SCOPE)[keyof typeof MCP_SCOPE];
80
+ export type McpServerState = {
81
+ name: string;
82
+ transport: McpTransport;
83
+ usable: boolean;
84
+ /** `null` when the CLI did not enumerate tools (unknown, not "none"). */
85
+ tools: readonly string[] | null;
86
+ };
87
+ export interface McpCapability {
88
+ list(ctx: HostContext): Promise<McpServerState[]>;
89
+ add: {
90
+ [T in McpTransport]?: (ctx: HostContext, name: string, spec: Extract<McpServerSpec, {
91
+ transport: T;
92
+ }>, options: {
93
+ scope: McpScope;
94
+ }) => Promise<InstallOutcome>;
95
+ };
96
+ remove(ctx: HostContext, name: string, options: {
97
+ scope: McpScope;
98
+ }): Promise<void>;
99
+ }
100
+ export declare const DOCTOR_CHECK_STATUS: {
101
+ readonly pass: "pass";
102
+ readonly fail: "fail";
103
+ };
104
+ export type DoctorCheck = {
105
+ id: string;
106
+ status: (typeof DOCTOR_CHECK_STATUS)[keyof typeof DOCTOR_CHECK_STATUS];
107
+ detail: string | null;
108
+ };
109
+ export type DoctorReport = {
110
+ available: boolean;
111
+ checks: DoctorCheck[];
112
+ };
113
+ export interface DoctorCapability {
114
+ probe(ctx: HostContext): Promise<DoctorReport>;
115
+ }
116
+ export declare const CAPABILITY_AVAILABILITY_STATUS: {
117
+ readonly available: "available";
118
+ readonly missing: "missing";
119
+ readonly unusable: "unusable";
120
+ readonly toolMissing: "tool-missing";
121
+ };
122
+ export type SkillAvailability = {
123
+ status: typeof CAPABILITY_AVAILABILITY_STATUS.available;
124
+ skill: InstalledSkill;
125
+ } | {
126
+ status: typeof CAPABILITY_AVAILABILITY_STATUS.missing;
127
+ };
128
+ export type McpAvailability = {
129
+ status: typeof CAPABILITY_AVAILABILITY_STATUS.available;
130
+ server: McpServerState;
131
+ } | {
132
+ status: typeof CAPABILITY_AVAILABILITY_STATUS.missing;
133
+ } | {
134
+ status: typeof CAPABILITY_AVAILABILITY_STATUS.unusable;
135
+ server: McpServerState;
136
+ } | {
137
+ status: typeof CAPABILITY_AVAILABILITY_STATUS.toolMissing;
138
+ server: McpServerState;
139
+ tool: string;
140
+ };
141
+ export declare function checkSkillAvailability(skills: readonly InstalledSkill[], id: string): SkillAvailability;
142
+ export declare function checkMcpAvailability(servers: readonly McpServerState[], requirement: {
143
+ server: string;
144
+ tool: string | null;
145
+ }): McpAvailability;
146
+ //# sourceMappingURL=capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../src/host/capabilities.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Host-plane capability contracts (design "Skills and MCP"). Keyed
3
+ * handler records per source kind / transport: presence = supported,
4
+ * absence = typed unsupported. Concrete implementations live on each
5
+ * adapter; the contracts and shared spec types live here.
6
+ */
7
+ export const SKILL_SOURCE_KIND = {
8
+ localDir: "local-dir",
9
+ githubDir: "github-dir",
10
+ plugin: "plugin",
11
+ };
12
+ export const INSTALL_STATUS = {
13
+ installed: "installed",
14
+ alreadyInstalled: "already-installed",
15
+ };
16
+ export const MCP_TRANSPORT = {
17
+ stdio: "stdio",
18
+ http: "http",
19
+ };
20
+ export const MCP_SCOPE = {
21
+ project: "project",
22
+ user: "user",
23
+ };
24
+ export const DOCTOR_CHECK_STATUS = {
25
+ pass: "pass",
26
+ fail: "fail",
27
+ };
28
+ export const CAPABILITY_AVAILABILITY_STATUS = {
29
+ available: "available",
30
+ missing: "missing",
31
+ unusable: "unusable",
32
+ toolMissing: "tool-missing",
33
+ };
34
+ export function checkSkillAvailability(skills, id) {
35
+ const skill = skills.find((candidate) => candidate.id === id || candidate.name === id) ??
36
+ null;
37
+ return skill === null
38
+ ? { status: CAPABILITY_AVAILABILITY_STATUS.missing }
39
+ : { status: CAPABILITY_AVAILABILITY_STATUS.available, skill };
40
+ }
41
+ export function checkMcpAvailability(servers, requirement) {
42
+ const server = servers.find((candidate) => candidate.name === requirement.server) ?? null;
43
+ if (server === null) {
44
+ return { status: CAPABILITY_AVAILABILITY_STATUS.missing };
45
+ }
46
+ if (!server.usable) {
47
+ return { status: CAPABILITY_AVAILABILITY_STATUS.unusable, server };
48
+ }
49
+ if (requirement.tool !== null &&
50
+ server.tools !== null &&
51
+ !server.tools.includes(requirement.tool)) {
52
+ return {
53
+ status: CAPABILITY_AVAILABILITY_STATUS.toolMissing,
54
+ server,
55
+ tool: requirement.tool,
56
+ };
57
+ }
58
+ return { status: CAPABILITY_AVAILABILITY_STATUS.available, server };
59
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Host-plane context (design "Skills and MCP"): the runtime owns the
3
+ * effectful parts and binds them once; the call site owns locations.
4
+ * Nothing here ever calls `os.homedir()`.
5
+ */
6
+ import type { Clock, Exec, HttpClient, Logger } from "../core/ports.js";
7
+ /** Call-site input (rule 1). */
8
+ export type HostLocation = {
9
+ homeDir: string;
10
+ projectDir?: string;
11
+ };
12
+ /** What capability implementations receive (rule 2). */
13
+ export type HostContext = {
14
+ location: {
15
+ homeDir: string;
16
+ projectDir: string | null;
17
+ };
18
+ env: Record<string, string>;
19
+ exec: Exec;
20
+ http: HttpClient;
21
+ /** For probe-owned time mechanics (timeouts) — mirrors RunContext. */
22
+ clock: Clock;
23
+ logger: Logger;
24
+ };
25
+ export declare function resolveHostLocation(location: HostLocation): {
26
+ homeDir: string;
27
+ projectDir: string | null;
28
+ };
29
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/host/context.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAExE,gCAAgC;AAChC,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,wDAAwD;AACxD,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACzD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;IACjB,sEAAsE;IACtE,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,YAAY,GAAG;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAKA"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Host-plane context (design "Skills and MCP"): the runtime owns the
3
+ * effectful parts and binds them once; the call site owns locations.
4
+ * Nothing here ever calls `os.homedir()`.
5
+ */
6
+ export function resolveHostLocation(location) {
7
+ return {
8
+ homeDir: location.homeDir,
9
+ projectDir: location.projectDir ?? null,
10
+ };
11
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * GitHub-directory fetch adapter for skill installation (design v D4):
3
+ * walk the GitHub contents API over the host `http` port and stage the
4
+ * directory tree into a temp dir. The staged tree then flows through the
5
+ * caller's local install pipeline — sources differ only in this fetch
6
+ * step, never in validation or copy. File contents stay `Buffer` from
7
+ * fetch to disk so binary assets survive staging byte-for-byte.
8
+ */
9
+ import type { SKILL_SOURCE_KIND, SkillSource } from "./capabilities.js";
10
+ import type { HostContext } from "./context.js";
11
+ export type GithubDirSource = Extract<SkillSource, {
12
+ kind: typeof SKILL_SOURCE_KIND.githubDir;
13
+ }>;
14
+ /** Stage the remote directory, hand it to `use`, always clean up. */
15
+ export declare function withStagedGithubDirectory<T>(ctx: HostContext, source: GithubDirSource, use: (stagedDir: string) => Promise<T>): Promise<T>;
16
+ //# sourceMappingURL=github-dir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-dir.d.ts","sourceRoot":"","sources":["../../src/host/github-dir.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,MAAM,eAAe,GAAG,OAAO,CACnC,WAAW,EACX;IAAE,IAAI,EAAE,OAAO,iBAAiB,CAAC,SAAS,CAAA;CAAE,CAC7C,CAAC;AAqKF,qEAAqE;AACrE,wBAAsB,yBAAyB,CAAC,CAAC,EAC/C,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,eAAe,EACvB,GAAG,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GACrC,OAAO,CAAC,CAAC,CAAC,CAWZ"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * GitHub-directory fetch adapter for skill installation (design v D4):
3
+ * walk the GitHub contents API over the host `http` port and stage the
4
+ * directory tree into a temp dir. The staged tree then flows through the
5
+ * caller's local install pipeline — sources differ only in this fetch
6
+ * step, never in validation or copy. File contents stay `Buffer` from
7
+ * fetch to disk so binary assets survive staging byte-for-byte.
8
+ */
9
+ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
10
+ import os from "node:os";
11
+ import path from "node:path";
12
+ import { AGENT_ERROR_CODE, AgentError } from "../core/error.js";
13
+ import { readString, readUnknownRecord, } from "../core/json.js";
14
+ const GITHUB_CONTENTS_API = "https://api.github.com/repos";
15
+ const STAGING_DIRECTORY_PREFIX = "agents-github-skill-";
16
+ const DEFAULT_SKILL_DIRECTORY_NAME = "skill";
17
+ const GITHUB_CONTENTS_FIELD = {
18
+ content: "content",
19
+ downloadUrl: "download_url",
20
+ encoding: "encoding",
21
+ name: "name",
22
+ path: "path",
23
+ type: "type",
24
+ };
25
+ const GITHUB_CONTENTS_ENTRY_TYPE = {
26
+ directory: "dir",
27
+ file: "file",
28
+ };
29
+ const GITHUB_CONTENT_ENCODING = {
30
+ base64: "base64",
31
+ };
32
+ function githubSourceDirectoryName(source) {
33
+ const normalized = source.path.replace(/\/+$/u, "");
34
+ if (normalized !== "") {
35
+ return path.basename(normalized);
36
+ }
37
+ const repoName = source.repo.split("/").at(-1);
38
+ return repoName === undefined || repoName === ""
39
+ ? DEFAULT_SKILL_DIRECTORY_NAME
40
+ : repoName;
41
+ }
42
+ function githubRepoParts(source) {
43
+ const parts = source.repo.split("/");
44
+ const [owner, repo] = parts;
45
+ if (parts.length !== 2 ||
46
+ owner === undefined ||
47
+ repo === undefined ||
48
+ owner === "" ||
49
+ repo === "") {
50
+ throw new AgentError({
51
+ code: AGENT_ERROR_CODE.invalidInput,
52
+ message: `Invalid GitHub repo "${source.repo}"`,
53
+ data: { reason: "github-dir repo must be owner/name" },
54
+ });
55
+ }
56
+ return [owner, repo];
57
+ }
58
+ function githubContentsUrl(source, remotePath) {
59
+ const [owner, repo] = githubRepoParts(source);
60
+ const encodedPath = remotePath
61
+ .split("/")
62
+ .filter((part) => part !== "")
63
+ .map(encodeURIComponent)
64
+ .join("/");
65
+ const pathPart = encodedPath === "" ? "" : `/${encodedPath}`;
66
+ return `${GITHUB_CONTENTS_API}/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents${pathPart}?ref=${encodeURIComponent(source.ref)}`;
67
+ }
68
+ async function fetchJson(ctx, url) {
69
+ const response = await ctx.http.fetch(url, {
70
+ // biome-ignore lint/style/useNamingConvention: HTTP header names keep their wire spelling.
71
+ headers: { Accept: "application/vnd.github+json" },
72
+ });
73
+ if (!response.ok) {
74
+ throw new AgentError({
75
+ code: AGENT_ERROR_CODE.failed,
76
+ message: `GitHub skill fetch failed with status ${response.status}`,
77
+ data: {
78
+ exitCode: null,
79
+ stdoutTail: null,
80
+ stderrTail: response.statusText || null,
81
+ },
82
+ });
83
+ }
84
+ return response.json();
85
+ }
86
+ async function fetchBytes(ctx, url) {
87
+ const response = await ctx.http.fetch(url);
88
+ if (!response.ok) {
89
+ throw new AgentError({
90
+ code: AGENT_ERROR_CODE.failed,
91
+ message: `GitHub skill file fetch failed with status ${response.status}`,
92
+ data: {
93
+ exitCode: null,
94
+ stdoutTail: null,
95
+ stderrTail: response.statusText || null,
96
+ },
97
+ });
98
+ }
99
+ return Buffer.from(await response.arrayBuffer());
100
+ }
101
+ async function writeGithubFile(ctx, entry, targetPath) {
102
+ const inlineContent = readString(entry, GITHUB_CONTENTS_FIELD.content);
103
+ const encoding = readString(entry, GITHUB_CONTENTS_FIELD.encoding);
104
+ let content;
105
+ if (inlineContent !== null && encoding === GITHUB_CONTENT_ENCODING.base64) {
106
+ content = Buffer.from(inlineContent.replace(/\s/gu, ""), GITHUB_CONTENT_ENCODING.base64);
107
+ }
108
+ else {
109
+ const downloadUrl = readString(entry, GITHUB_CONTENTS_FIELD.downloadUrl);
110
+ if (downloadUrl === null) {
111
+ throw new AgentError({
112
+ code: AGENT_ERROR_CODE.failed,
113
+ message: "GitHub skill file is missing download_url",
114
+ data: { exitCode: null, stdoutTail: null, stderrTail: null },
115
+ });
116
+ }
117
+ content = await fetchBytes(ctx, downloadUrl);
118
+ }
119
+ await mkdir(path.dirname(targetPath), { recursive: true });
120
+ await writeFile(targetPath, content);
121
+ }
122
+ async function fetchGithubDirectory(ctx, source, remotePath, targetDir) {
123
+ const payload = await fetchJson(ctx, githubContentsUrl(source, remotePath));
124
+ if (!Array.isArray(payload)) {
125
+ throw new AgentError({
126
+ code: AGENT_ERROR_CODE.invalidInput,
127
+ message: `GitHub skill source is not a directory: ${remotePath}`,
128
+ data: { reason: "github-dir source must resolve to a directory" },
129
+ });
130
+ }
131
+ await mkdir(targetDir, { recursive: true });
132
+ for (const rawEntry of payload) {
133
+ const entry = readUnknownRecord(rawEntry);
134
+ if (entry === null) {
135
+ continue;
136
+ }
137
+ const entryType = readString(entry, GITHUB_CONTENTS_FIELD.type);
138
+ const entryName = readString(entry, GITHUB_CONTENTS_FIELD.name);
139
+ const entryPath = readString(entry, GITHUB_CONTENTS_FIELD.path);
140
+ if (entryName === null || entryPath === null) {
141
+ continue;
142
+ }
143
+ const targetPath = path.join(targetDir, entryName);
144
+ if (entryType === GITHUB_CONTENTS_ENTRY_TYPE.directory) {
145
+ await fetchGithubDirectory(ctx, source, entryPath, targetPath);
146
+ continue;
147
+ }
148
+ if (entryType === GITHUB_CONTENTS_ENTRY_TYPE.file) {
149
+ await writeGithubFile(ctx, entry, targetPath);
150
+ }
151
+ }
152
+ }
153
+ /** Stage the remote directory, hand it to `use`, always clean up. */
154
+ export async function withStagedGithubDirectory(ctx, source, use) {
155
+ const stagingParent = await mkdtemp(path.join(os.tmpdir(), STAGING_DIRECTORY_PREFIX));
156
+ const stagedDir = path.join(stagingParent, githubSourceDirectoryName(source));
157
+ try {
158
+ await fetchGithubDirectory(ctx, source, source.path, stagedDir);
159
+ return await use(stagedDir);
160
+ }
161
+ finally {
162
+ await rm(stagingParent, { recursive: true, force: true });
163
+ }
164
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Shared `mcp list` inventory: run the agent CLI with `--json`, parse the
3
+ * output into `McpServerState[]`. Parsing is tolerant of the shapes the
4
+ * claude/codex CLIs emit (array, `{servers: [...]}` or name-keyed maps),
5
+ * but non-JSON output is a typed failure, never a raw `SyntaxError`.
6
+ */
7
+ import { type McpServerState } from "./capabilities.js";
8
+ import type { HostContext } from "./context.js";
9
+ export declare function listMcpServersViaCli(ctx: HostContext, input: {
10
+ command: string;
11
+ }): Promise<McpServerState[]>;
12
+ //# sourceMappingURL=mcp-list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-list.d.ts","sourceRoot":"","sources":["../../src/host/mcp-list.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAyGhD,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GACzB,OAAO,CAAC,cAAc,EAAE,CAAC,CAsB3B"}