@pikiloom/kernel 0.2.20 → 0.3.1
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/README.md +20 -7
- package/dist/drivers/acp.js +30 -163
- package/dist/drivers/claude.d.ts +4 -21
- package/dist/drivers/claude.js +88 -57
- package/dist/drivers/codex.d.ts +0 -10
- package/dist/drivers/codex.js +47 -144
- package/dist/drivers/gemini.js +9 -27
- package/dist/drivers/hermes.d.ts +1 -2
- package/dist/drivers/hermes.js +1 -4
- package/dist/drivers/index.d.ts +5 -4
- package/dist/drivers/index.js +8 -4
- package/dist/{workspace → drivers}/native.d.ts +0 -1
- package/dist/{workspace → drivers}/native.js +1 -1
- package/dist/drivers/rpc.d.ts +45 -0
- package/dist/drivers/rpc.js +130 -0
- package/dist/drivers/shared.d.ts +21 -0
- package/dist/drivers/shared.js +66 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +10 -2
- package/dist/protocol/index.d.ts +5 -0
- package/dist/protocol/index.js +10 -0
- package/dist/runtime/hub.js +4 -8
- package/dist/workspace/index.d.ts +1 -2
- package/dist/workspace/index.js +3 -2
- package/dist/workspace/mcp.js +6 -16
- package/dist/workspace/npm-search.d.ts +9 -0
- package/dist/workspace/npm-search.js +21 -0
- package/dist/workspace/sessions.js +6 -9
- package/dist/workspace/skills.d.ts +11 -0
- package/dist/workspace/skills.js +14 -20
- package/llms.txt +11 -3
- package/package.json +1 -1
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
|
|
3
|
-
const i = sessionKey.indexOf(':');
|
|
4
|
-
return i < 0 ? { agent: '', sessionId: sessionKey } : { agent: sessionKey.slice(0, i), sessionId: sessionKey.slice(i + 1) };
|
|
5
|
-
}
|
|
2
|
+
import { makeSessionKey, splitSessionKey } from '../protocol/index.js';
|
|
6
3
|
function ts(iso) {
|
|
7
4
|
if (!iso)
|
|
8
5
|
return 0;
|
|
@@ -14,7 +11,7 @@ function newer(a, b) {
|
|
|
14
11
|
}
|
|
15
12
|
function managedToInfo(agent, rec) {
|
|
16
13
|
return {
|
|
17
|
-
sessionKey:
|
|
14
|
+
sessionKey: makeSessionKey(agent, rec.sessionId),
|
|
18
15
|
agent,
|
|
19
16
|
sessionId: rec.sessionId,
|
|
20
17
|
title: rec.title ?? null,
|
|
@@ -31,7 +28,7 @@ function managedToInfo(agent, rec) {
|
|
|
31
28
|
}
|
|
32
29
|
function nativeToInfo(agent, n) {
|
|
33
30
|
return {
|
|
34
|
-
sessionKey:
|
|
31
|
+
sessionKey: makeSessionKey(agent, n.sessionId),
|
|
35
32
|
agent,
|
|
36
33
|
sessionId: n.sessionId,
|
|
37
34
|
title: n.title,
|
|
@@ -72,7 +69,7 @@ export class SessionsManager {
|
|
|
72
69
|
if (!rec.workdir || path.resolve(rec.workdir) !== workdir)
|
|
73
70
|
continue;
|
|
74
71
|
}
|
|
75
|
-
byKey.set(
|
|
72
|
+
byKey.set(makeSessionKey(agent, rec.sessionId), managedToInfo(agent, rec));
|
|
76
73
|
}
|
|
77
74
|
}
|
|
78
75
|
// Native sessions (the agents' own stores) — inherently per-workdir.
|
|
@@ -89,7 +86,7 @@ export class SessionsManager {
|
|
|
89
86
|
this.deps.log?.(`[sessions] ${agent}.listNativeSessions failed: ${e?.message || e}`);
|
|
90
87
|
}
|
|
91
88
|
for (const n of natives) {
|
|
92
|
-
const key =
|
|
89
|
+
const key = makeSessionKey(agent, n.sessionId);
|
|
93
90
|
const existing = byKey.get(key);
|
|
94
91
|
if (existing) {
|
|
95
92
|
// Same identity discovered both ways: managed record wins, but adopt the newer
|
|
@@ -118,7 +115,7 @@ export class SessionsManager {
|
|
|
118
115
|
return typeof opts.limit === 'number' ? matched.slice(0, Math.max(0, opts.limit)) : matched;
|
|
119
116
|
}
|
|
120
117
|
async get(sessionKey, opts = {}) {
|
|
121
|
-
const { agent, sessionId } =
|
|
118
|
+
const { agent, sessionId } = splitSessionKey(sessionKey);
|
|
122
119
|
if (!agent)
|
|
123
120
|
return null;
|
|
124
121
|
const rec = await this.deps.store.get(agent, sessionId).catch(() => null);
|
|
@@ -21,6 +21,17 @@ export interface SkillsManagerOptions {
|
|
|
21
21
|
agentSkillDirs?: string[];
|
|
22
22
|
log?: (msg: string) => void;
|
|
23
23
|
}
|
|
24
|
+
export interface SkillMeta {
|
|
25
|
+
label: string | null;
|
|
26
|
+
description: string | null;
|
|
27
|
+
mcpRequires?: string[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parse a SKILL.md's frontmatter (label/name + description + mcp_requires), falling back
|
|
31
|
+
* to the first `# heading` for the label. Exported so an app scanning additional skill
|
|
32
|
+
* stores (e.g. an agent's own ~/.claude/skills) reads them with the same rules.
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseSkillMeta(content: string): SkillMeta;
|
|
24
35
|
/** Make `linkPath` a symlink to `targetDir` (idempotent; replaces a stale link/dir). */
|
|
25
36
|
export declare function ensureDirSymlink(linkPath: string, targetDir: string): void;
|
|
26
37
|
export declare class SkillsManager {
|
package/dist/workspace/skills.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { searchNpmPackages } from './npm-search.js';
|
|
3
4
|
const DEFAULT_AGENT_SKILL_DIRS = ['.claude/skills', '.agents/skills'];
|
|
4
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Parse a SKILL.md's frontmatter (label/name + description + mcp_requires), falling back
|
|
7
|
+
* to the first `# heading` for the label. Exported so an app scanning additional skill
|
|
8
|
+
* stores (e.g. an agent's own ~/.claude/skills) reads them with the same rules.
|
|
9
|
+
*/
|
|
10
|
+
export function parseSkillMeta(content) {
|
|
5
11
|
let label = null;
|
|
6
12
|
let description = null;
|
|
7
13
|
let mcpRequires;
|
|
8
14
|
const fm = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
9
15
|
if (fm) {
|
|
10
|
-
|
|
16
|
+
// Both frontmatter dialects: pikiloom's `label:` and the agent-native `name:`.
|
|
17
|
+
const lm = fm[1].match(/^label:\s*(.+)/m) || fm[1].match(/^name:\s*(.+)/m);
|
|
11
18
|
if (lm)
|
|
12
19
|
label = lm[1].trim();
|
|
13
20
|
const dm = fm[1].match(/^description:\s*(.+)/m);
|
|
@@ -145,25 +152,12 @@ export class SkillsManager {
|
|
|
145
152
|
/** Search installable skills on the npm registry (best-effort; [] on failure). */
|
|
146
153
|
async search(query, limit = 20) {
|
|
147
154
|
const q = (query || '').trim();
|
|
148
|
-
const text = encodeURIComponent(`agent skill ${q}`.trim());
|
|
149
|
-
const url = `https://registry.npmjs.org/-/v1/search?text=${text}&size=${Math.max(1, Math.min(50, limit))}`;
|
|
150
155
|
try {
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return objects.map((o) => {
|
|
157
|
-
const pkg = o?.package ?? {};
|
|
158
|
-
return {
|
|
159
|
-
name: String(pkg.name ?? ''),
|
|
160
|
-
description: pkg.description ?? null,
|
|
161
|
-
source: 'npm',
|
|
162
|
-
author: pkg.publisher?.username ?? pkg.author?.name ?? null,
|
|
163
|
-
homepage: pkg.links?.homepage ?? pkg.links?.npm ?? null,
|
|
164
|
-
version: pkg.version ?? null,
|
|
165
|
-
};
|
|
166
|
-
}).filter(s => s.name);
|
|
156
|
+
const hits = await searchNpmPackages(`agent skill ${q}`.trim(), Math.max(1, Math.min(50, limit)), fetch);
|
|
157
|
+
return hits.map(h => ({
|
|
158
|
+
name: h.name, description: h.description, source: 'npm',
|
|
159
|
+
author: h.author, homepage: h.homepage, version: h.version,
|
|
160
|
+
}));
|
|
167
161
|
}
|
|
168
162
|
catch (e) {
|
|
169
163
|
this.log?.(`[skills] search failed: ${e?.message || e}`);
|
package/llms.txt
CHANGED
|
@@ -59,6 +59,7 @@ interface UniversalSnapshot {
|
|
|
59
59
|
}
|
|
60
60
|
```
|
|
61
61
|
Wire delta helpers: `diffSnapshot(prev,next) => SnapshotPatch` (prefix-append text/reasoning; undefined→null clears) and `applySnapshotPatch(prev,patch)`. `emptySnapshot()`, `PROTOCOL_VERSION`.
|
|
62
|
+
Session keys: a sessionKey is `${agent}:${sessionId}` — compose/parse ONLY via `makeSessionKey(agent, sessionId)` / `splitSessionKey(sessionKey)`.
|
|
62
63
|
|
|
63
64
|
## Control verbs (LoomIO, via loom.io)
|
|
64
65
|
|
|
@@ -86,7 +87,8 @@ governs a session index, a skills registry and per-agent symlinks, resolved in t
|
|
|
86
87
|
- `loom.skills: SkillsManager` — the canonical skills registry. `canonicalDir(scope,wd?)` = `<root>/skills`;
|
|
87
88
|
`ensureLinks(scope, wd?)` symlinks it into each agent's dir (default `.claude/skills`, `.agents/skills`,
|
|
88
89
|
overridable via `agentSkillDirs`) so a skill registered once is visible to every agent. `list({workdir?,scope?})`,
|
|
89
|
-
`search(query) => npm results`.
|
|
90
|
+
`search(query) => npm results`. `parseSkillMeta(content)` (exported) reads a SKILL.md's frontmatter
|
|
91
|
+
(label/name + description + mcp_requires) — reuse it when scanning additional skill stores yourself.
|
|
90
92
|
- `loom.mcp: McpRegistry` — `recommended(): McpCatalogEntry[]` (curated), `search(query)` (MCP registry → npm),
|
|
91
93
|
`toServerSpec(entry, env?) => McpServerSpec`. Enabling/injecting a server stays on the Plugin.tools()/ToolProvider seam.
|
|
92
94
|
|
|
@@ -95,12 +97,18 @@ Drivers expose native discovery via the optional `AgentDriver.listNativeSessions
|
|
|
95
97
|
|
|
96
98
|
## Drivers (pass to createLoom({drivers}) or loom.registerDriver)
|
|
97
99
|
|
|
98
|
-
- ClaudeDriver (id 'claude'): `claude` CLI stream-json, supports --effort; steer ✓, resume ✓, tui
|
|
100
|
+
- ClaudeDriver (id 'claude'): `claude` CLI stream-json, supports --effort; steer ✓, resume ✓, tui ✓.
|
|
101
|
+
Background-hold/stall/recovery heuristics tunable via PIKILOOM_CLAUDE_* env (see README).
|
|
99
102
|
- CodexDriver (id 'codex'): `codex app-server` JSON-RPC; steer ✓, HITL via askUser, resume ✓, tui ✓
|
|
100
103
|
- GeminiDriver (id 'gemini'): `gemini` stream-json; resume ✓, tui ✓
|
|
101
|
-
-
|
|
104
|
+
- AcpDriver (id from config): generic ACP ndjson JSON-RPC — any ACP CLI via `new AcpDriver({ id, command, args })`; HITL via askUser, resume ✓
|
|
105
|
+
- HermesDriver (id 'hermes'): preset `new AcpDriver({ id:'hermes', command:'hermes', args:['acp'] })`; HITL via askUser, resume ✓
|
|
102
106
|
- EchoDriver (id 'echo'): hermetic in-process test driver; all caps ✓
|
|
103
107
|
|
|
108
|
+
Multi-account: `accountTokenSupported(agent)` / `accountTokenEnvVar(agent)` / `accountTokenEnv(agent, token)` —
|
|
109
|
+
which env var carries an agent's auth token (claude: CLAUDE_CODE_OAUTH_TOKEN), so an app injects a selected
|
|
110
|
+
account's token per spawn (via ModelResolver env or a Plugin contributeSpawn). Storage/selection stay app-side.
|
|
111
|
+
|
|
104
112
|
Write a driver: implement `AgentDriver { id; capabilities?; run(input, ctx); tui?(input) }`. In run(), call `ctx.emit(event)`:
|
|
105
113
|
`DriverEvent = {type:'session',sessionId} | {type:'text',delta} | {type:'reasoning',delta} | {type:'tool',call:UniversalToolCall} | {type:'plan',plan} | {type:'subagent',subagent} | {type:'usage',usage} | {type:'artifact',artifact} | {type:'activity',line}`.
|
|
106
114
|
`ctx = { signal: AbortSignal, emit, askUser(interaction)=>Promise<answers>, registerSteer(fn) }`. Return `DriverResult { ok, text, reasoning?, error?, stopReason?, sessionId?, usage? }`. Emit a `tool` event on BOTH start (status 'running') and completion (status 'done'/'failed'); the kernel projects `activity` from these.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikiloom/kernel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Heterogeneous coding agents (Claude / Codex / Gemini / ACP) -> an interaction-friendly, accumulating session snapshot + control handle, over pluggable surfaces (IM, Web, tunnel, TUI). The reusable core pikiloom itself is built on.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|