pikiloom 0.4.39 → 0.4.41
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/dashboard/dist/assets/{AgentTab-Ds8D6yrl.js → AgentTab-B4ZC9QFL.js} +1 -1
- package/dashboard/dist/assets/{ConnectionModal-Buj2d5L5.js → ConnectionModal-CAlACYKM.js} +1 -1
- package/dashboard/dist/assets/{DirBrowser-BTv7rH8E.js → DirBrowser-D3KGIof1.js} +1 -1
- package/dashboard/dist/assets/{ExtensionsTab-BnGwxS9U.js → ExtensionsTab-C_69Y7CR.js} +1 -1
- package/dashboard/dist/assets/{IMAccessTab-DjQnpOmF.js → IMAccessTab-B5_6IS4X.js} +1 -1
- package/dashboard/dist/assets/{Modal-DpuinsE3.js → Modal-BjZifpga.js} +1 -1
- package/dashboard/dist/assets/{Modals-Bs842H3n.js → Modals-B_V24pNA.js} +1 -1
- package/dashboard/dist/assets/{Select-Dvia29HZ.js → Select-D7xW38wq.js} +1 -1
- package/dashboard/dist/assets/SessionPanel-BbrfUDLg.js +1 -0
- package/dashboard/dist/assets/{SystemTab-BzPtwDxq.js → SystemTab-i5nmYweA.js} +1 -1
- package/dashboard/dist/assets/{index-S0NmlDEH.js → index-A_dL4aEz.js} +13 -13
- package/dashboard/dist/assets/{index-yZ-iG1qk.js → index-D18pCeqv.js} +3 -3
- package/dashboard/dist/assets/{shared-p3kZpiD4.js → shared-BKwEgcmy.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dist/agent/drivers/codex.js +5 -3
- package/dist/agent/index.js +1 -1
- package/dist/agent/stream.js +13 -1
- package/dist/bot/render-shared.js +2 -1
- package/dist/core/config/runtime-config.js +14 -22
- package/dist/dashboard/routes/agents.js +8 -4
- package/package.json +1 -1
- package/packages/kernel/README.md +35 -0
- package/packages/kernel/dist/contracts/driver.d.ts +15 -0
- package/packages/kernel/dist/contracts/ports.d.ts +2 -0
- package/packages/kernel/dist/drivers/claude.d.ts +5 -1
- package/packages/kernel/dist/drivers/claude.js +21 -0
- package/packages/kernel/dist/drivers/codex.d.ts +22 -1
- package/packages/kernel/dist/drivers/codex.js +87 -7
- package/packages/kernel/dist/drivers/gemini.d.ts +5 -1
- package/packages/kernel/dist/drivers/gemini.js +4 -0
- package/packages/kernel/dist/index.d.ts +2 -1
- package/packages/kernel/dist/index.js +2 -0
- package/packages/kernel/dist/ports/defaults.js +7 -1
- package/packages/kernel/dist/runtime/loom.d.ts +10 -0
- package/packages/kernel/dist/runtime/loom.js +18 -2
- package/packages/kernel/dist/workspace/index.d.ts +5 -0
- package/packages/kernel/dist/workspace/index.js +7 -0
- package/packages/kernel/dist/workspace/mcp.d.ts +44 -0
- package/packages/kernel/dist/workspace/mcp.js +82 -0
- package/packages/kernel/dist/workspace/native.d.ts +14 -0
- package/packages/kernel/dist/workspace/native.js +340 -0
- package/packages/kernel/dist/workspace/paths.d.ts +33 -0
- package/packages/kernel/dist/workspace/paths.js +40 -0
- package/packages/kernel/dist/workspace/sessions.d.ts +49 -0
- package/packages/kernel/dist/workspace/sessions.js +130 -0
- package/packages/kernel/dist/workspace/skills.d.ts +44 -0
- package/packages/kernel/dist/workspace/skills.js +173 -0
- package/dashboard/dist/assets/SessionPanel-CVItEgcH.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as s}from"./react-vendor-C7Sl8SE7.js";import{N as t,m as i,S as a}from"./index-
|
|
1
|
+
import{j as s}from"./react-vendor-C7Sl8SE7.js";import{N as t,m as i,S as a}from"./index-D18pCeqv.js";function o({primary:e,secondary:l,tertiary:n}){return s.jsxs("div",{className:"flex flex-col gap-2 pt-1 sm:flex-row sm:flex-wrap sm:items-center sm:justify-between",children:[s.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e&&s.jsxs(i,{tone:"primary",onClick:e.onClick,disabled:e.disabled,children:[e.loading&&s.jsx(a,{}),e.label]}),l&&s.jsx(i,{tone:"secondary",onClick:l.onClick,disabled:l.disabled,children:l.label})]}),n&&s.jsx("div",{className:"text-xs leading-relaxed text-fg-4",children:n})]})}function c({children:e,className:l}){return s.jsx(t,{padding:"md",elevation:"flat",className:l,children:e})}export{o as A,c as S};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<link rel="icon" type="image/png" href="/logo.png">
|
|
7
7
|
<title>Pikiloom</title>
|
|
8
8
|
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-D18pCeqv.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/react-vendor-C7Sl8SE7.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/router-DHISdpPk.js">
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/assets/index-Bthwt6K_.css">
|
|
@@ -34,9 +34,11 @@ export class CodexAppServer {
|
|
|
34
34
|
return new Promise((resolve) => {
|
|
35
35
|
const timer = setTimeout(() => { this.kill(); resolve(false); }, CODEX_APPSERVER_SPAWN_TIMEOUT_MS);
|
|
36
36
|
const args = ['app-server'];
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
// Do NOT force model_reasoning_summary — reasoning summaries stay OFF by default (respecting
|
|
38
|
+
// ~/.codex/config.toml = original behavior). A caller wanting thinking passes it via configOverrides.
|
|
39
|
+
const overrides = [...this.configOverrides];
|
|
40
|
+
if (!overrides.some(entry => /^features\.goals\s*=/.test(entry)))
|
|
41
|
+
overrides.push('features.goals=true');
|
|
40
42
|
for (const c of overrides)
|
|
41
43
|
args.push('-c', c);
|
|
42
44
|
agentLog(`[codex-rpc] spawning: codex ${args.join(' ')}`);
|
package/dist/agent/index.js
CHANGED
|
@@ -7,7 +7,7 @@ export { attachAgentImage, attachInlineImage, materializeImage, rewriteAttachmen
|
|
|
7
7
|
export { deliverArtifact, readDeliveredArtifacts, deliveredArtifactBlocks, tailDeliveredBlocks, latestDeliveredTaskId, mimeForArtifact, } from './artifacts.js';
|
|
8
8
|
export { Q, agentLog, agentWarn, agentError, dedupeStrings, numberOrNull, normalizeStreamPreviewPlan, parseTodoWriteAsPlan, normalizeActivityLine, pushRecentActivity, detectClaudeApiError, isRetryableClaudeApiError, detectClaudeModelError, claudeModelErrorMessage, firstNonEmptyLine, shortValue, normalizeErrorMessage, joinErrorMessages, appendSystemPrompt, mimeForExt, computeContext, buildStreamPreviewMeta, summarizeClaudeToolUse, summarizeClaudeToolResult, previewToolCallInput, previewToolCallResult, roundPercent, toIsoFromEpochSeconds, normalizeUsageStatus, labelFromWindowMinutes, usageWindowFromRateLimit, parseJsonTail, modelFamily, normalizeClaudeModelId, emptyUsage, readTailLines, stripInjectedPrompts, sanitizeSessionUserPreviewText, SESSION_PREVIEW_IMAGE_PLACEHOLDER_RE, CLAUDE_AT_MENTION_IMAGE_RE, extractClaudeAtMentionImagePaths, stripClaudeAtMentionImages, isPendingSessionId, emitSessionIdUpdate, sessionListDisplayTitle, } from './utils.js';
|
|
9
9
|
export { updateSessionMeta, promoteSessionId, recordFork, resolveCanonicalSessionId, getSessionPromotions, listPikiloomSessions, findPikiloomSession, getSessionStoredConfig, ensureManagedSession, findManagedThreadSession, stageSessionFiles, mergeManagedAndNativeSessions, managedRecordToSessionInfo, getSessions, getSessionTail, getSessionMessages, applyTurnWindow, applyTurnFilter, classifySession, deriveUserStatus, exportSession, importSession, deleteAgentSession, isProcessAlive, isRunningSessionStale, reconcileOrphanedRunningSessions, } from './session.js';
|
|
10
|
-
export { detectAgentBin, listAgents, resolveDefaultAgent, run, doStream, recoverProfileIdForModel, listModels, resolveAgentModels, getUsage, getAgentBoundModelId, setAgentBoundModelId, } from './stream.js';
|
|
10
|
+
export { detectAgentBin, listAgents, resolveDefaultAgent, run, doStream, recoverProfileIdForModel, listModels, resolveAgentModels, dropNativeShadowedByProfiles, getUsage, getAgentBoundModelId, setAgentBoundModelId, } from './stream.js';
|
|
11
11
|
export { registerDriver, getDriver, getDriverCapabilities, allDrivers, allDriverIds, hasDriver, shutdownAllDrivers, } from './driver.js';
|
|
12
12
|
export { getProjectSkillPaths, initializeProjectSkills, listSkills, getGlobalSkillsRoot, collapseSkillPrompt, } from './skills.js';
|
|
13
13
|
export { readGoal, writeGoal, clearGoal, setGoal, pauseGoal, resumeGoal, completeGoal, accountTurn, bumpContinuationCount, shouldContinueAfterTurn, renderContinuationPrompt, renderBudgetLimitPrompt, sessionGoalPath, DEFAULT_MAX_CONTINUATIONS, } from './goal.js';
|
package/dist/agent/stream.js
CHANGED
|
@@ -658,6 +658,17 @@ export function listModels(agent, opts = {}) {
|
|
|
658
658
|
function isLocalProviderBaseURL(baseURL) {
|
|
659
659
|
return /^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\])(?::|\/|$)/i.test(baseURL);
|
|
660
660
|
}
|
|
661
|
+
// A model surfaced as a BYOK profile must NOT also appear under the native group. Drivers seed
|
|
662
|
+
// their native list with the active model (via opts.currentModel) so it stays visible — but when
|
|
663
|
+
// a BYOK profile is active that "current model" is the profile's third-party model (e.g. a 豆包 /
|
|
664
|
+
// deepseek id), which then wrongly shows up as an "official / 官方 / Native" model. The profile row
|
|
665
|
+
// already represents it, so drop any native entry shadowed by a profile id.
|
|
666
|
+
export function dropNativeShadowedByProfiles(native, profileRows) {
|
|
667
|
+
if (!profileRows.length)
|
|
668
|
+
return native;
|
|
669
|
+
const shadowed = new Set(profileRows.map(m => m.id.trim().toLowerCase()).filter(Boolean));
|
|
670
|
+
return native.filter(m => !shadowed.has(m.id.trim().toLowerCase()));
|
|
671
|
+
}
|
|
661
672
|
export async function resolveAgentModels(agent, opts = {}) {
|
|
662
673
|
const driver = getDriver(agent);
|
|
663
674
|
let nativeResult;
|
|
@@ -667,7 +678,7 @@ export async function resolveAgentModels(agent, opts = {}) {
|
|
|
667
678
|
catch {
|
|
668
679
|
nativeResult = { agent, models: [], sources: [], note: null };
|
|
669
680
|
}
|
|
670
|
-
const
|
|
681
|
+
const nativeRaw = nativeResult.models.map(m => ({
|
|
671
682
|
id: m.id,
|
|
672
683
|
alias: m.alias,
|
|
673
684
|
group: 'native',
|
|
@@ -693,6 +704,7 @@ export async function resolveAgentModels(agent, opts = {}) {
|
|
|
693
704
|
(isLocal ? local : cloud).push(entry);
|
|
694
705
|
}
|
|
695
706
|
}
|
|
707
|
+
const native = dropNativeShadowedByProfiles(nativeRaw, [...cloud, ...local]);
|
|
696
708
|
const sources = [...nativeResult.sources];
|
|
697
709
|
if (cloud.length)
|
|
698
710
|
sources.push(`${cloud.length} cloud profile${cloud.length === 1 ? '' : 's'}`);
|
|
@@ -3,6 +3,7 @@ import { fmtUptime, formatThinkingForDisplay, thinkLabel } from './bot.js';
|
|
|
3
3
|
import { formatActivityCommandSummary, parseActivitySummary, renderPlanForPreview, summarizeActivityForPreview } from './streaming.js';
|
|
4
4
|
import { supportsChannelCapability } from '../channels/base.js';
|
|
5
5
|
import { agentLog, agentWarn } from '../agent/index.js';
|
|
6
|
+
import { effortLabel } from '../core/config/runtime-config.js';
|
|
6
7
|
export function parseGfmTable(tableLines) {
|
|
7
8
|
if (tableLines.length < 3)
|
|
8
9
|
return null;
|
|
@@ -53,7 +54,7 @@ export function formatFooterParts(agent, elapsedMs, meta, contextPercent, decora
|
|
|
53
54
|
identityParts.push(compactModelLabel(modelLabel));
|
|
54
55
|
const runtimeParts = [];
|
|
55
56
|
if (decorations?.effort)
|
|
56
|
-
runtimeParts.push(decorations.effort);
|
|
57
|
+
runtimeParts.push(effortLabel(decorations.effort));
|
|
57
58
|
const ctx = contextPercent ?? meta?.contextPercent ?? null;
|
|
58
59
|
if (ctx != null)
|
|
59
60
|
runtimeParts.push(`${ctx}%`);
|
|
@@ -164,34 +164,26 @@ export function decomposeEffortSelection(raw) {
|
|
|
164
164
|
return { effort: 'max', workflow: true };
|
|
165
165
|
return { effort: value, workflow: false };
|
|
166
166
|
}
|
|
167
|
+
// The one canonical, human-facing name for an effort token. The picker (effortOptionsFor →
|
|
168
|
+
// dashboard composer + IM /models), the dashboard turn header, and the IM agent-status row all
|
|
169
|
+
// render this, so a level reads identically on every surface — no upper layer relabels it. We
|
|
170
|
+
// surface the raw token (low/medium/high/xhigh/max/ultra/minimal), not a prose label like
|
|
171
|
+
// "Very High", to match the id used in config, CLI flags and logs. Change presentation here only.
|
|
172
|
+
export function effortLabel(id) {
|
|
173
|
+
return (id ?? '').trim().toLowerCase();
|
|
174
|
+
}
|
|
167
175
|
// Single source of truth for reasoning-effort levels, ordered low→high. BOTH the dashboard
|
|
168
176
|
// (which receives them via the agent/model API payload) and the IM bot consume this — do not
|
|
169
177
|
// reintroduce a second copy. Resolve options through effortOptionsFor(), never index directly,
|
|
170
|
-
// so per-model/provider rules stay in one place.
|
|
178
|
+
// so per-model/provider rules stay in one place. Labels come from effortLabel() so the picker
|
|
179
|
+
// and the live status row can never diverge.
|
|
180
|
+
const effortLevels = (...ids) => ids.map(id => ({ id, label: effortLabel(id) }));
|
|
171
181
|
const AGENT_EFFORT_LEVELS = {
|
|
172
|
-
claude:
|
|
173
|
-
|
|
174
|
-
{ id: 'medium', label: 'Medium' },
|
|
175
|
-
{ id: 'high', label: 'High' },
|
|
176
|
-
{ id: 'xhigh', label: 'Very High' },
|
|
177
|
-
{ id: 'max', label: 'Max' },
|
|
178
|
-
{ id: ULTRA_EFFORT, label: 'Ultra' },
|
|
179
|
-
],
|
|
180
|
-
codex: [
|
|
181
|
-
{ id: 'low', label: 'Low' },
|
|
182
|
-
{ id: 'medium', label: 'Medium' },
|
|
183
|
-
{ id: 'high', label: 'High' },
|
|
184
|
-
{ id: 'xhigh', label: 'Very High' },
|
|
185
|
-
],
|
|
182
|
+
claude: effortLevels('low', 'medium', 'high', 'xhigh', 'max', ULTRA_EFFORT),
|
|
183
|
+
codex: effortLevels('low', 'medium', 'high', 'xhigh'),
|
|
186
184
|
// gemini intentionally has no UI-exposed effort levels: pikiloom sends it no reasoning-effort
|
|
187
185
|
// (see the gemini→null guards in InputComposer). Add a gemini entry here to surface low/high.
|
|
188
|
-
hermes:
|
|
189
|
-
{ id: 'minimal', label: 'Minimal' },
|
|
190
|
-
{ id: 'low', label: 'Low' },
|
|
191
|
-
{ id: 'medium', label: 'Medium' },
|
|
192
|
-
{ id: 'high', label: 'High' },
|
|
193
|
-
{ id: 'xhigh', label: 'Very High' },
|
|
194
|
-
],
|
|
186
|
+
hermes: effortLevels('minimal', 'low', 'medium', 'high', 'xhigh'),
|
|
195
187
|
};
|
|
196
188
|
// Valid effort levels for a given (agent, model, providerKind). Returns [] when reasoning
|
|
197
189
|
// effort does not apply (the UI then hides the selector entirely). model/providerKind are the
|
|
@@ -137,6 +137,11 @@ async function buildAgentStatusResponse(config = loadUserConfig(), agentOptions
|
|
|
137
137
|
}
|
|
138
138
|
const runtimeSelectedModel = runtime.getRuntimeModel(agentId, config);
|
|
139
139
|
const runtimeSelectedEffort = runtime.getRuntimeEffort(agentId, config);
|
|
140
|
+
const activeProfile = getActiveProfile(agentId);
|
|
141
|
+
// runtimeSelectedModel is the model that will RUN (profile-aware): when a BYOK profile is
|
|
142
|
+
// bound it is the profile's third-party model. The NATIVE model list must not be seeded with
|
|
143
|
+
// it, else it shows under the "官方 / Native" group (it is surfaced separately via byokModels).
|
|
144
|
+
const nativeRuntimeModel = activeProfile ? '' : runtimeSelectedModel;
|
|
140
145
|
let models = [];
|
|
141
146
|
let usage = emptyUsage(agentId, 'Agent not installed.');
|
|
142
147
|
let nativeConfig = null;
|
|
@@ -149,10 +154,10 @@ async function buildAgentStatusResponse(config = loadUserConfig(), agentOptions
|
|
|
149
154
|
}
|
|
150
155
|
catch { }
|
|
151
156
|
}
|
|
152
|
-
const modelFallback =
|
|
157
|
+
const modelFallback = nativeRuntimeModel ? [{ id: nativeRuntimeModel, alias: null }] : [];
|
|
153
158
|
const cachedUsage = driver.getUsage({ agent: agentId, model: runtimeSelectedModel });
|
|
154
159
|
const [resolvedModels, resolvedUsage] = await Promise.all([
|
|
155
|
-
withTimeoutFallback(driver.listModels({ workdir, currentModel:
|
|
160
|
+
withTimeoutFallback(driver.listModels({ workdir, currentModel: nativeRuntimeModel || undefined }).then(result => dedupeModels([
|
|
156
161
|
...modelFallback,
|
|
157
162
|
...result.models,
|
|
158
163
|
])), AGENT_STATUS_MODELS_TIMEOUT_MS, modelFallback),
|
|
@@ -169,14 +174,13 @@ async function buildAgentStatusResponse(config = loadUserConfig(), agentOptions
|
|
|
169
174
|
}
|
|
170
175
|
}
|
|
171
176
|
const updateState = getAgentUpdateState(agentId);
|
|
172
|
-
const activeProfile = getActiveProfile(agentId);
|
|
173
177
|
const byokProvider = activeProfile ? getProvider(activeProfile.providerId) : null;
|
|
174
178
|
const byokProviderName = byokProvider?.name || null;
|
|
175
179
|
const byokProfileName = activeProfile
|
|
176
180
|
&& activeProfile.name.trim().toLowerCase() !== activeProfile.modelId.trim().toLowerCase()
|
|
177
181
|
? activeProfile.name
|
|
178
182
|
: null;
|
|
179
|
-
const nativeSelectedModel =
|
|
183
|
+
const nativeSelectedModel = nativeRuntimeModel || nativeConfig?.model || null;
|
|
180
184
|
const nativeSelectedEffort = runtimeSelectedEffort || nativeConfig?.effort || null;
|
|
181
185
|
const selectedModel = activeProfile?.modelId || nativeSelectedModel;
|
|
182
186
|
const selectedEffort = activeProfile?.effort || nativeSelectedEffort;
|
package/package.json
CHANGED
|
@@ -275,6 +275,40 @@ plugins are the composable per-capability layer on top.)
|
|
|
275
275
|
|
|
276
276
|
---
|
|
277
277
|
|
|
278
|
+
## Workspace: unified directory + session / skill / mcp management
|
|
279
|
+
|
|
280
|
+
One explicitly-configurable **top-level directory** (`createLoom({ stateDirName })`, default
|
|
281
|
+
`'pikiloom'` → `.pikiloom`) gives a consuming app the same "everything under one folder" model
|
|
282
|
+
pikiloom uses, resolved in two scopes — global (`~/.pikiloom`) and per-workspace
|
|
283
|
+
(`<workdir>/.pikiloom`). It's exposed off the `Loom`:
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
const loom = createLoom({ drivers: [new ClaudeDriver(), new CodexDriver()], stateDirName: 'pikiloom' });
|
|
287
|
+
|
|
288
|
+
// Unified, searchable session list — the kernel's MANAGED sessions (scoped per workspace by the
|
|
289
|
+
// cwd they ran in) MERGED with each agent's OWN native sessions (claude/codex read their on-disk
|
|
290
|
+
// transcripts). Global view, per-workspace view, and search — all kernel-owned:
|
|
291
|
+
await loom.sessions.list({ scope: 'workspace', workdir }); // this folder (managed + native)
|
|
292
|
+
await loom.sessions.list({ scope: 'global' }); // every workspace's managed sessions
|
|
293
|
+
await loom.sessions.search({ query: 'deploy', workdir });
|
|
294
|
+
|
|
295
|
+
// Skills registry: one canonical dir, symlinked into every agent's skills dir.
|
|
296
|
+
loom.skills.ensureLinks('workspace', workdir); // <wd>/.claude/skills + .agents/skills → <wd>/.pikiloom/skills
|
|
297
|
+
loom.skills.list({ workdir }); // installed skills (workspace + global)
|
|
298
|
+
await loom.skills.search('pdf'); // installable skills (npm)
|
|
299
|
+
|
|
300
|
+
// MCP catalog + discovery (enabling a server stays on the Plugin.tools()/ToolProvider seam):
|
|
301
|
+
loom.mcp.recommended(); // curated catalog
|
|
302
|
+
await loom.mcp.search('postgres'); // MCP registry → npm
|
|
303
|
+
|
|
304
|
+
loom.paths.skillsDir('global'); // ~/.pikiloom/skills, etc.
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
A driver opts into native discovery by implementing
|
|
308
|
+
`listNativeSessions?({ workdir, limit }): NativeSessionInfo[]` (Claude/Codex/Gemini do; the pure
|
|
309
|
+
readers are also exported as `discover{Claude,Codex,Gemini}NativeSessions`). All of this is
|
|
310
|
+
node-builtins-only and additive — every existing port/default is unchanged.
|
|
311
|
+
|
|
278
312
|
## Exports
|
|
279
313
|
|
|
280
314
|
Main entry `@pikiloom/kernel` re-exports everything. Subpaths: `@pikiloom/kernel/drivers`,
|
|
@@ -284,6 +318,7 @@ Main entry `@pikiloom/kernel` re-exports everything. Subpaths: `@pikiloom/kernel
|
|
|
284
318
|
- Drivers: `EchoDriver`, `ClaudeDriver`, `CodexDriver`, `GeminiDriver`, `HermesDriver`
|
|
285
319
|
- Surfaces: `WebSurface`, `CliSurface`
|
|
286
320
|
- Ports/defaults: `FsSessionStore`, `NullModelResolver`, `NoopToolProvider`, `PassthroughSystemPromptBuilder`, `AutoCancelInteractionHandler`, `DeferToTerminalInteractionHandler`, `NoopCatalog`, `defaultBaseDir`
|
|
321
|
+
- Workspace: `resolveLoomPaths`, `SessionsManager`, `SkillsManager`, `McpRegistry`, `ensureDirSymlink`, `discover{Claude,Codex,Gemini}NativeSessions` + types `LoomPaths`, `LoomScope`, `ManagedSessionInfo`, `NativeSessionInfo`, `SkillInfo`, `McpCatalogEntry`
|
|
287
322
|
- Protocol: `UniversalSnapshot`, `diffSnapshot`, `applySnapshotPatch`, `emptySnapshot`, `PROTOCOL_VERSION`, all wire/`Client*`/`Server*` message types
|
|
288
323
|
- Types: `AgentDriver`, `AgentTurnInput`, `DriverContext`, `DriverEvent`, `DriverResult`, `LoomIO`, `PromptInput`, `Surface`, `Plugin`, `SpawnContribution`, `SessionStore`, `ModelResolver`, `ToolProvider`, `SystemPromptBuilder`, `InteractionHandler`, `Catalog`, …
|
|
289
324
|
|
|
@@ -82,6 +82,17 @@ export interface TuiSpec {
|
|
|
82
82
|
cwd: string;
|
|
83
83
|
env?: Record<string, string>;
|
|
84
84
|
}
|
|
85
|
+
export interface NativeSessionInfo {
|
|
86
|
+
sessionId: string;
|
|
87
|
+
title: string | null;
|
|
88
|
+
preview: string | null;
|
|
89
|
+
cwd: string | null;
|
|
90
|
+
model: string | null;
|
|
91
|
+
createdAt: string | null;
|
|
92
|
+
updatedAt: string | null;
|
|
93
|
+
running: boolean;
|
|
94
|
+
messageCount?: number | null;
|
|
95
|
+
}
|
|
85
96
|
export interface AgentDriver {
|
|
86
97
|
readonly id: string;
|
|
87
98
|
readonly capabilities?: {
|
|
@@ -92,4 +103,8 @@ export interface AgentDriver {
|
|
|
92
103
|
};
|
|
93
104
|
run(input: AgentTurnInput, ctx: DriverContext): Promise<DriverResult>;
|
|
94
105
|
tui?(input: TuiInput): TuiSpec;
|
|
106
|
+
listNativeSessions?(opts: {
|
|
107
|
+
workdir: string;
|
|
108
|
+
limit?: number;
|
|
109
|
+
}): NativeSessionInfo[] | Promise<NativeSessionInfo[]>;
|
|
95
110
|
}
|
|
@@ -5,9 +5,11 @@ export interface CoreSessionRecord {
|
|
|
5
5
|
sessionId: string;
|
|
6
6
|
nativeSessionId?: string | null;
|
|
7
7
|
workspacePath: string;
|
|
8
|
+
workdir?: string | null;
|
|
8
9
|
createdAt: string;
|
|
9
10
|
updatedAt: string;
|
|
10
11
|
title?: string | null;
|
|
12
|
+
preview?: string | null;
|
|
11
13
|
model?: string | null;
|
|
12
14
|
effort?: string | null;
|
|
13
15
|
runState?: 'running' | 'completed' | 'incomplete';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentDriver, AgentTurnInput, DriverContext, DriverResult, DriverEvent, TuiInput, TuiSpec } from '../contracts/driver.js';
|
|
1
|
+
import type { AgentDriver, AgentTurnInput, DriverContext, DriverResult, DriverEvent, TuiInput, TuiSpec, NativeSessionInfo } from '../contracts/driver.js';
|
|
2
2
|
import type { UniversalUsage, UniversalPlan } from '../protocol/index.js';
|
|
3
3
|
export declare class ClaudeDriver implements AgentDriver {
|
|
4
4
|
private readonly bin;
|
|
@@ -11,6 +11,10 @@ export declare class ClaudeDriver implements AgentDriver {
|
|
|
11
11
|
};
|
|
12
12
|
constructor(bin?: string);
|
|
13
13
|
tui(input: TuiInput): TuiSpec;
|
|
14
|
+
listNativeSessions(opts: {
|
|
15
|
+
workdir: string;
|
|
16
|
+
limit?: number;
|
|
17
|
+
}): NativeSessionInfo[];
|
|
14
18
|
run(input: AgentTurnInput, ctx: DriverContext): Promise<DriverResult>;
|
|
15
19
|
private usage;
|
|
16
20
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
+
import { discoverClaudeNativeSessions } from '../workspace/native.js';
|
|
2
3
|
// Real driver: shells the local `claude` CLI in stream-json mode and normalizes its
|
|
3
4
|
// events into kernel DriverEvents. Faithful to pikiloom's claude.ts event shapes
|
|
4
5
|
// (system / stream_event{message_start,content_block_delta,message_delta} / assistant / result),
|
|
@@ -22,6 +23,9 @@ export class ClaudeDriver {
|
|
|
22
23
|
args.push(...input.extraArgs);
|
|
23
24
|
return { command: this.bin, args, cwd: input.workdir, env: input.env };
|
|
24
25
|
}
|
|
26
|
+
listNativeSessions(opts) {
|
|
27
|
+
return discoverClaudeNativeSessions(opts.workdir, { limit: opts.limit });
|
|
28
|
+
}
|
|
25
29
|
run(input, ctx) {
|
|
26
30
|
const steerable = !!input.steerable;
|
|
27
31
|
const args = ['-p', '--verbose', '--output-format', 'stream-json', '--include-partial-messages'];
|
|
@@ -232,6 +236,23 @@ export function handleClaudeEvent(ev, s, emit) {
|
|
|
232
236
|
s.cacheCreation = u?.cache_creation_input_tokens ?? 0;
|
|
233
237
|
s.output = 0;
|
|
234
238
|
}
|
|
239
|
+
else if (inner.type === 'content_block_start') {
|
|
240
|
+
// Claude emits multiple text/thinking blocks per turn (one set per tool-use round). Insert a
|
|
241
|
+
// paragraph break before a NEW block when prior content exists, so the live preview shows
|
|
242
|
+
// breaks between segments instead of running them together. Mirrors the legacy driver; the
|
|
243
|
+
// separator is emitted as a delta so the runtime's accumulated snapshot stays in sync with s.
|
|
244
|
+
const bt = inner.content_block?.type;
|
|
245
|
+
if (bt === 'text' && s.text && !s.text.endsWith('\n\n')) {
|
|
246
|
+
const sep = s.text.endsWith('\n') ? '\n' : '\n\n';
|
|
247
|
+
s.text += sep;
|
|
248
|
+
emit({ type: 'text', delta: sep });
|
|
249
|
+
}
|
|
250
|
+
else if (bt === 'thinking' && s.reasoning && !s.reasoning.endsWith('\n\n')) {
|
|
251
|
+
const sep = s.reasoning.endsWith('\n') ? '\n' : '\n\n';
|
|
252
|
+
s.reasoning += sep;
|
|
253
|
+
emit({ type: 'reasoning', delta: sep });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
235
256
|
else if (inner.type === 'content_block_delta') {
|
|
236
257
|
const d = inner.delta || {};
|
|
237
258
|
if (d.type === 'text_delta' && d.text) {
|
|
@@ -1,5 +1,22 @@
|
|
|
1
|
-
import type { AgentDriver, AgentTurnInput, DriverContext, DriverResult, TuiInput, TuiSpec } from '../contracts/driver.js';
|
|
1
|
+
import type { AgentDriver, AgentTurnInput, DriverContext, DriverEvent, DriverResult, TuiInput, TuiSpec, NativeSessionInfo } from '../contracts/driver.js';
|
|
2
2
|
import type { UniversalUsage } from '../protocol/index.js';
|
|
3
|
+
export declare function codexToolSummary(item: any): {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
summary: string;
|
|
7
|
+
} | null;
|
|
8
|
+
export interface CodexContentState {
|
|
9
|
+
text: string;
|
|
10
|
+
reasoning: string;
|
|
11
|
+
streamedReasoning: boolean;
|
|
12
|
+
msgs: string[];
|
|
13
|
+
thinkParts: string[];
|
|
14
|
+
}
|
|
15
|
+
export declare function codexReasoningItemText(item: any): string;
|
|
16
|
+
export declare function captureCodexAgentMessage(item: any, s: CodexContentState, deltaItems: Set<string>, phases: Map<string, string>, emit: (e: DriverEvent) => void): void;
|
|
17
|
+
export declare function captureCodexReasoning(text: string, s: CodexContentState, emit: (e: DriverEvent) => void): void;
|
|
18
|
+
export declare function codexFinalText(s: CodexContentState): string;
|
|
19
|
+
export declare function codexFinalReasoning(s: CodexContentState): string;
|
|
3
20
|
export declare class CodexDriver implements AgentDriver {
|
|
4
21
|
private readonly bin;
|
|
5
22
|
readonly id = "codex";
|
|
@@ -12,6 +29,10 @@ export declare class CodexDriver implements AgentDriver {
|
|
|
12
29
|
constructor(bin?: string);
|
|
13
30
|
run(input: AgentTurnInput, ctx: DriverContext): Promise<DriverResult>;
|
|
14
31
|
tui(input: TuiInput): TuiSpec;
|
|
32
|
+
listNativeSessions(opts: {
|
|
33
|
+
workdir: string;
|
|
34
|
+
limit?: number;
|
|
35
|
+
}): NativeSessionInfo[];
|
|
15
36
|
}
|
|
16
37
|
export interface CodexUsageState {
|
|
17
38
|
input: number | null;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
+
import { discoverCodexNativeSessions } from '../workspace/native.js';
|
|
2
3
|
// Minimal newline-delimited JSON-RPC client for `codex app-server` (ported faithfully
|
|
3
4
|
// from pikiloom's CodexAppServer; trimmed to what the kernel needs).
|
|
4
5
|
class AppServer {
|
|
@@ -20,7 +21,12 @@ class AppServer {
|
|
|
20
21
|
onServerRequest(cb) { this.requestResponder = cb; }
|
|
21
22
|
async start() {
|
|
22
23
|
const args = ['app-server'];
|
|
23
|
-
|
|
24
|
+
// Do NOT force model_reasoning_summary — codex reasoning summaries stay OFF by default
|
|
25
|
+
// (respecting ~/.codex/config.toml, which is the original behavior). A caller that wants
|
|
26
|
+
// thinking can pass `model_reasoning_summary=...` via configOverrides; we never inject it.
|
|
27
|
+
const overrides = [...this.configOverrides];
|
|
28
|
+
if (!overrides.some(c => /^features\.goals\s*=/.test(c)))
|
|
29
|
+
overrides.push('features.goals=true');
|
|
24
30
|
for (const c of overrides)
|
|
25
31
|
args.push('-c', c);
|
|
26
32
|
try {
|
|
@@ -126,7 +132,12 @@ function codexFileChangeSummary(item) {
|
|
|
126
132
|
}
|
|
127
133
|
// Normalize a codex app-server item -> {id, name, summary}. The summary mirrors pikiloom's
|
|
128
134
|
// vocabulary ("Run shell: <cmd>", "Edit <path>") so the activity projection reads naturally.
|
|
129
|
-
|
|
135
|
+
// Only ACTUAL tool calls become Activity rows. agentMessage (the answer text) and reasoning
|
|
136
|
+
// (thinking) are CONTENT — they render below as message text / thinking, never as tools. The old
|
|
137
|
+
// fallback to `item.type` wrongly turned every item (incl. agentMessage/reasoning) into a bogus
|
|
138
|
+
// "tool" in the Activity card. Mirrors the legacy driver's isCodexToolCallItem whitelist.
|
|
139
|
+
const CODEX_TOOL_CALL_TYPES = new Set(['dynamicToolCall', 'mcpToolCall', 'collabAgentToolCall']);
|
|
140
|
+
export function codexToolSummary(item) {
|
|
130
141
|
const id = String(item?.id || '');
|
|
131
142
|
if (!id)
|
|
132
143
|
return null;
|
|
@@ -136,8 +147,13 @@ function codexToolSummary(item) {
|
|
|
136
147
|
}
|
|
137
148
|
if (item.type === 'fileChange' || item.type === 'patch')
|
|
138
149
|
return { id, name: 'edit', summary: codexFileChangeSummary(item) };
|
|
139
|
-
|
|
140
|
-
|
|
150
|
+
if (CODEX_TOOL_CALL_TYPES.has(item.type)) {
|
|
151
|
+
const raw = typeof item.tool === 'string' && item.tool.trim() ? item.tool.trim()
|
|
152
|
+
: typeof item.name === 'string' && item.name.trim() ? item.name.trim() : '';
|
|
153
|
+
const name = raw ? (raw.split('.').pop() || raw) : 'tool';
|
|
154
|
+
return { id, name, summary: raw ? `Use ${name}` : 'Use tool' };
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
141
157
|
}
|
|
142
158
|
// codex `item/tool/requestUserInput` params -> a normalized UniversalInteraction
|
|
143
159
|
// (faithful to the legacy codex driver's toAgentInteraction shape).
|
|
@@ -160,6 +176,51 @@ function codexUserInputToInteraction(params, promptId) {
|
|
|
160
176
|
return null;
|
|
161
177
|
return { promptId, kind: 'user-input', title: 'User Input Required', hint: 'Use the buttons when available, or reply with text.', questions };
|
|
162
178
|
}
|
|
179
|
+
// Reasoning text from a completed `reasoning` item (item/completed or rawResponseItem/completed):
|
|
180
|
+
// summary/content arrays of plain strings or {text} objects.
|
|
181
|
+
export function codexReasoningItemText(item) {
|
|
182
|
+
const parts = [
|
|
183
|
+
...(Array.isArray(item?.summary) ? item.summary : []),
|
|
184
|
+
...(Array.isArray(item?.content) ? item.content : []),
|
|
185
|
+
];
|
|
186
|
+
return parts.map((p) => (typeof p === 'string' ? p : p?.text || '')).filter(Boolean).join('\n').trim();
|
|
187
|
+
}
|
|
188
|
+
// A completed final_answer agentMessage that did NOT stream deltas: append + emit it live.
|
|
189
|
+
// deltaItems holds the ids already streamed, so a completed item echoing a streamed one is
|
|
190
|
+
// not double-counted (matches the legacy driver's deltaSeenForItem guard).
|
|
191
|
+
export function captureCodexAgentMessage(item, s, deltaItems, phases, emit) {
|
|
192
|
+
const phase = item?.phase || (item?.id ? phases.get(item.id) : null) || 'final_answer';
|
|
193
|
+
if (phase !== 'final_answer')
|
|
194
|
+
return;
|
|
195
|
+
const text = typeof item?.text === 'string' ? item.text.trim() : '';
|
|
196
|
+
if (!text)
|
|
197
|
+
return;
|
|
198
|
+
s.msgs.push(text);
|
|
199
|
+
if (item.id && deltaItems.has(item.id))
|
|
200
|
+
return;
|
|
201
|
+
const delta = s.text.trim() ? `\n\n${text}` : text;
|
|
202
|
+
s.text += delta;
|
|
203
|
+
emit({ type: 'text', delta });
|
|
204
|
+
}
|
|
205
|
+
// A completed reasoning item: keep it for the end-of-turn fallback, and stream it live only
|
|
206
|
+
// when nothing arrived as reasoning deltas (so the streamed path is never double-emitted).
|
|
207
|
+
export function captureCodexReasoning(text, s, emit) {
|
|
208
|
+
if (!text)
|
|
209
|
+
return;
|
|
210
|
+
s.thinkParts.push(text);
|
|
211
|
+
if (s.streamedReasoning)
|
|
212
|
+
return;
|
|
213
|
+
const delta = s.reasoning.trim() ? `\n\n${text}` : text;
|
|
214
|
+
s.reasoning += delta;
|
|
215
|
+
emit({ type: 'reasoning', delta });
|
|
216
|
+
}
|
|
217
|
+
// End-of-turn finalizers: prefer streamed text/reasoning, fall back to completed-item parts.
|
|
218
|
+
export function codexFinalText(s) {
|
|
219
|
+
return s.text.trim() ? s.text : s.msgs.join('\n\n');
|
|
220
|
+
}
|
|
221
|
+
export function codexFinalReasoning(s) {
|
|
222
|
+
return s.reasoning.trim() ? s.reasoning : s.thinkParts.join('\n\n');
|
|
223
|
+
}
|
|
163
224
|
export class CodexDriver {
|
|
164
225
|
bin;
|
|
165
226
|
id = 'codex';
|
|
@@ -173,9 +234,10 @@ export class CodexDriver {
|
|
|
173
234
|
// to the native account.
|
|
174
235
|
const config = [...(input.configOverrides || [])];
|
|
175
236
|
const srv = new AppServer(this.bin, config, input.env);
|
|
176
|
-
const state = { text: '', reasoning: '', sessionId: input.sessionId ?? null, input: null, output: null, cached: null, contextUsed: null, contextWindow: null, status: null, error: null, turnId: null };
|
|
237
|
+
const state = { text: '', reasoning: '', streamedReasoning: false, msgs: [], thinkParts: [], sessionId: input.sessionId ?? null, input: null, output: null, cached: null, contextUsed: null, contextWindow: null, status: null, error: null, turnId: null };
|
|
177
238
|
const phases = new Map();
|
|
178
239
|
const toolSummaries = new Map();
|
|
240
|
+
const deltaItems = new Set();
|
|
179
241
|
let steerRegistered = false;
|
|
180
242
|
const ok = await srv.start();
|
|
181
243
|
if (!ok)
|
|
@@ -239,6 +301,8 @@ export class CodexDriver {
|
|
|
239
301
|
const phase = params?.itemId ? (phases.get(params.itemId) || 'final_answer') : 'final_answer';
|
|
240
302
|
if (phase === 'final_answer' && params?.delta) {
|
|
241
303
|
state.text += params.delta;
|
|
304
|
+
if (params.itemId)
|
|
305
|
+
deltaItems.add(params.itemId);
|
|
242
306
|
ctx.emit({ type: 'text', delta: params.delta });
|
|
243
307
|
}
|
|
244
308
|
break;
|
|
@@ -247,16 +311,28 @@ export class CodexDriver {
|
|
|
247
311
|
case 'item/reasoning/summaryTextDelta':
|
|
248
312
|
if (params?.delta) {
|
|
249
313
|
state.reasoning += params.delta;
|
|
314
|
+
state.streamedReasoning = true;
|
|
250
315
|
ctx.emit({ type: 'reasoning', delta: params.delta });
|
|
251
316
|
}
|
|
252
317
|
break;
|
|
253
318
|
case 'item/completed': {
|
|
254
319
|
const item = params?.item || {};
|
|
320
|
+
// Final answer / reasoning delivered as a completed item (no preceding deltas).
|
|
321
|
+
if (item.type === 'agentMessage')
|
|
322
|
+
captureCodexAgentMessage(item, state, deltaItems, phases, ctx.emit);
|
|
323
|
+
else if (item.type === 'reasoning')
|
|
324
|
+
captureCodexReasoning(codexReasoningItemText(item), state, ctx.emit);
|
|
255
325
|
const t = codexToolSummary(item);
|
|
256
326
|
if (t && toolSummaries.has(t.id))
|
|
257
327
|
ctx.emit({ type: 'tool', call: { id: t.id, name: t.name, summary: toolSummaries.get(t.id) || t.summary, status: item.status === 'failed' ? 'failed' : 'done' } });
|
|
258
328
|
break;
|
|
259
329
|
}
|
|
330
|
+
case 'rawResponseItem/completed': {
|
|
331
|
+
const item = params?.item || {};
|
|
332
|
+
if (item?.type === 'reasoning')
|
|
333
|
+
captureCodexReasoning(codexReasoningItemText(item), state, ctx.emit);
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
260
336
|
case 'turn/plan/updated': {
|
|
261
337
|
const plan = planFromUpdate(params);
|
|
262
338
|
if (plan)
|
|
@@ -307,10 +383,11 @@ export class CodexDriver {
|
|
|
307
383
|
await turnDone;
|
|
308
384
|
const usage = codexUsageOf(state);
|
|
309
385
|
const ok2 = (state.status === 'completed' || state.status == null) && !state.error && !ctx.signal.aborted;
|
|
386
|
+
const finalReasoning = codexFinalReasoning(state);
|
|
310
387
|
return {
|
|
311
388
|
ok: ok2,
|
|
312
|
-
text: state
|
|
313
|
-
reasoning:
|
|
389
|
+
text: codexFinalText(state),
|
|
390
|
+
reasoning: finalReasoning || undefined,
|
|
314
391
|
error: state.error || (ctx.signal.aborted ? 'Interrupted by user.' : null),
|
|
315
392
|
stopReason: ctx.signal.aborted ? 'interrupted' : (state.status || 'end_turn'),
|
|
316
393
|
sessionId: state.sessionId,
|
|
@@ -329,6 +406,9 @@ export class CodexDriver {
|
|
|
329
406
|
args.push(...input.extraArgs);
|
|
330
407
|
return { command: this.bin, args, cwd: input.workdir, env: input.env };
|
|
331
408
|
}
|
|
409
|
+
listNativeSessions(opts) {
|
|
410
|
+
return discoverCodexNativeSessions(opts.workdir, { limit: opts.limit });
|
|
411
|
+
}
|
|
332
412
|
}
|
|
333
413
|
const IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.webp']);
|
|
334
414
|
function buildTurnInput(prompt, attachments) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentDriver, AgentTurnInput, DriverContext, DriverResult, DriverEvent, TuiInput, TuiSpec } from '../contracts/driver.js';
|
|
1
|
+
import type { AgentDriver, AgentTurnInput, DriverContext, DriverResult, DriverEvent, TuiInput, TuiSpec, NativeSessionInfo } from '../contracts/driver.js';
|
|
2
2
|
export declare class GeminiDriver implements AgentDriver {
|
|
3
3
|
private readonly bin;
|
|
4
4
|
readonly id = "gemini";
|
|
@@ -11,6 +11,10 @@ export declare class GeminiDriver implements AgentDriver {
|
|
|
11
11
|
constructor(bin?: string);
|
|
12
12
|
run(input: AgentTurnInput, ctx: DriverContext): Promise<DriverResult>;
|
|
13
13
|
tui(input: TuiInput): TuiSpec;
|
|
14
|
+
listNativeSessions(opts: {
|
|
15
|
+
workdir: string;
|
|
16
|
+
limit?: number;
|
|
17
|
+
}): NativeSessionInfo[];
|
|
14
18
|
}
|
|
15
19
|
export declare function parseGeminiEvent(ev: any, s: any, tools: Map<string, {
|
|
16
20
|
name: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
+
import { discoverGeminiNativeSessions } from '../workspace/native.js';
|
|
2
3
|
// Native kernel Gemini driver: `gemini --output-format stream-json ... -p <prompt>` and
|
|
3
4
|
// parse its stream-json events into kernel DriverEvents. Faithful to pikiloom's geminiParse.
|
|
4
5
|
export class GeminiDriver {
|
|
@@ -80,6 +81,9 @@ export class GeminiDriver {
|
|
|
80
81
|
args.push(...input.extraArgs);
|
|
81
82
|
return { command: this.bin, args, cwd: input.workdir, env: input.env };
|
|
82
83
|
}
|
|
84
|
+
listNativeSessions(opts) {
|
|
85
|
+
return discoverGeminiNativeSessions(opts.workdir, { limit: opts.limit });
|
|
86
|
+
}
|
|
83
87
|
}
|
|
84
88
|
export function parseGeminiEvent(ev, s, tools, emit) {
|
|
85
89
|
const t = ev.type || '';
|
|
@@ -4,7 +4,7 @@ export { SessionRunner } from './runtime/session-runner.js';
|
|
|
4
4
|
export { runTurn, type RunTurnOptions, type TurnOutcome } from './runtime/turn.js';
|
|
5
5
|
export { PtyBridge, ptyAvailable, type PtyExit, type PtyOpenOpts } from './runtime/pty.js';
|
|
6
6
|
export { attachTui, type AttachTuiOptions } from './runtime/tui.js';
|
|
7
|
-
export type { AgentDriver, AgentTurnInput, DriverContext, DriverEvent, DriverResult, SteerFn, McpServerSpec, TuiInput, TuiSpec, } from './contracts/driver.js';
|
|
7
|
+
export type { AgentDriver, AgentTurnInput, DriverContext, DriverEvent, DriverResult, SteerFn, McpServerSpec, TuiInput, TuiSpec, NativeSessionInfo, } from './contracts/driver.js';
|
|
8
8
|
export type { SessionStore, CoreSessionRecord, ModelResolver, ModelInjection, ToolProvider, SystemPromptBuilder, InteractionHandler, Catalog, } from './contracts/ports.js';
|
|
9
9
|
export type { LoomIO, PromptInput, Surface, SurfaceCapabilities, Plugin, SpawnContribution, } from './contracts/surface.js';
|
|
10
10
|
export { FsSessionStore, NullModelResolver, NoopToolProvider, PassthroughSystemPromptBuilder, AutoCancelInteractionHandler, DeferToTerminalInteractionHandler, NoopCatalog, defaultBaseDir, } from './ports/defaults.js';
|
|
@@ -15,4 +15,5 @@ export { GeminiDriver } from './drivers/gemini.js';
|
|
|
15
15
|
export { HermesDriver } from './drivers/hermes.js';
|
|
16
16
|
export { WebSurface, type WebSurfaceOptions } from './surfaces/web.js';
|
|
17
17
|
export { CliSurface } from './surfaces/cli.js';
|
|
18
|
+
export * from './workspace/index.js';
|
|
18
19
|
export * from './protocol/index.js';
|
|
@@ -25,5 +25,7 @@ export { GeminiDriver } from './drivers/gemini.js';
|
|
|
25
25
|
export { HermesDriver } from './drivers/hermes.js';
|
|
26
26
|
export { WebSurface } from './surfaces/web.js';
|
|
27
27
|
export { CliSurface } from './surfaces/cli.js';
|
|
28
|
+
// Workspace: unified top-level directory + session/skill/mcp management (loom.paths/sessions/skills/mcp)
|
|
29
|
+
export * from './workspace/index.js';
|
|
28
30
|
// Protocol (the wire vocabulary; shared with transports)
|
|
29
31
|
export * from './protocol/index.js';
|