This commit is contained in:
Oleg Maslov
2026-09-02 10:14:22 +02:00
parent 0c3e2ead3b
commit b20b138fe4
771 changed files with 161561 additions and 9027 deletions

View File

@@ -124,10 +124,12 @@ export const createResourceSchema = z.object({
config: z.record(z.unknown()),
});
export const scheduledJobTypeSchema = z.enum(['chat', 'task', 'waggle', 'group']);
export const createCronSchema = z.object({
name: z.string().min(1).max(200),
cronExpr: z.string().min(1),
jobType: z.string().min(1),
jobType: scheduledJobTypeSchema,
jobConfig: z.record(z.unknown()).default({}),
});

View File

@@ -17,8 +17,8 @@
*/
/**
* Canonical AI-tool IDs that hive-mind has shipped hook packages for.
* See packages/hive-mind-hooks-* for the matching installers.
* Canonical built-in AI-tool execution surfaces. CLI and desktop surfaces use
* distinct ids whenever they have different launch/task capabilities.
*/
export const SUPPORTED_TOOLS = [
'claude-code',
@@ -27,6 +27,7 @@ export const SUPPORTED_TOOLS = [
'codex',
'codex-desktop',
'hermes',
'hermes-desktop',
'openclaw',
] as const;
@@ -74,6 +75,8 @@ export interface ToolCapabilities {
liveWaggleDance: boolean;
}
export type ToolReleaseStatus = 'supported' | 'roadmap';
/**
* AI-OS #5 — declarative descriptor for one external tool. The single source of
* truth for the per-tool facts that used to be duplicated across SUPPORTED_TOOLS
@@ -84,8 +87,12 @@ export interface ToolCapabilities {
export interface ToolManifest {
id: string;
displayName: string;
/** Omitted means supported; roadmap integrations remain detectable but inert. */
releaseStatus?: ToolReleaseStatus;
launchable: boolean;
hookCapable: boolean;
/** Built-in-only pointer root; third-party adapters always use the user home. */
hookRoot?: 'user-home' | 'hermes-home';
hookPointer: string;
detect: ToolDetectSpec;
/**
@@ -98,12 +105,13 @@ export interface ToolManifest {
capabilities?: ToolCapabilities;
/** Present only when the adapter has a verified, capturable headless lane. */
task?: ToolTaskSpec;
/** true = first-party (the 7); false/absent = loaded third-party. */
/** true = first-party built-in; false/absent = loaded third-party. */
builtin?: boolean;
}
/**
* The canonical 7 built-in tools — the source of truth for their per-tool data.
* The canonical 8 built-in execution surfaces — the source of truth for their
* per-tool data.
* SUPPORTED_TOOLS (above) stays the `as const` type anchor; the cohort/name/
* pointer consts derive from these manifests.
*/
@@ -113,8 +121,8 @@ export const BUILTIN_TOOL_MANIFESTS: readonly ToolManifest[] = [
hookPointer: '.claude/hive-mind-install.json', detect: { kind: 'path', binaryName: 'claude' }, builtin: true,
capabilities: { interactiveLaunch: true, headlessTask: true, structuredProgress: true, resumable: true, liveWaggleDance: false },
task: {
argvTemplate: ['-p', '--safe-mode', '--disable-slash-commands', '--no-session-persistence', '--max-budget-usd', '0.25', '--input-format', 'text', '--output-format', 'stream-json', '--verbose', '{accessArgs}'],
resumeArgvTemplate: ['-p', '--safe-mode', '--disable-slash-commands', '--resume', '{sessionId}', '--max-budget-usd', '0.25', '--input-format', 'text', '--output-format', 'stream-json', '--verbose', '{accessArgs}'],
argvTemplate: ['-p', '--safe-mode', '--disable-slash-commands', '--max-budget-usd', '1.00', '--input-format', 'text', '--output-format', 'stream-json', '--verbose', '{accessArgs}'],
resumeArgvTemplate: ['-p', '--safe-mode', '--disable-slash-commands', '--resume', '{sessionId}', '--max-budget-usd', '1.00', '--input-format', 'text', '--output-format', 'stream-json', '--verbose', '{accessArgs}'],
accessArgs: {
'read-only': ['--permission-mode', 'plan'],
'workspace-write': ['--permission-mode', 'acceptEdits'],
@@ -130,7 +138,7 @@ export const BUILTIN_TOOL_MANIFESTS: readonly ToolManifest[] = [
capabilities: { interactiveLaunch: true, headlessTask: false, structuredProgress: false, resumable: false, liveWaggleDance: false },
},
{
id: 'cursor', displayName: 'Cursor', launchable: true, hookCapable: true,
id: 'cursor', displayName: 'Cursor', releaseStatus: 'roadmap', launchable: false, hookCapable: false,
hookPointer: '.cursor/hive-mind-install.json', detect: { kind: 'candidates' }, builtin: true,
capabilities: { interactiveLaunch: true, headlessTask: false, structuredProgress: false, resumable: false, liveWaggleDance: false },
},
@@ -139,8 +147,8 @@ export const BUILTIN_TOOL_MANIFESTS: readonly ToolManifest[] = [
hookPointer: '.codex/hive-mind-install.json', detect: { kind: 'path', binaryName: 'codex' }, builtin: true,
capabilities: { interactiveLaunch: true, headlessTask: true, structuredProgress: true, resumable: true, liveWaggleDance: false },
task: {
argvTemplate: ['{accessArgs}', 'exec', '--ignore-user-config', '--ignore-rules', '--ephemeral', '--skip-git-repo-check', '--json', '--color', 'never', '-C', '{workspacePath}', '-'],
resumeArgvTemplate: ['{accessArgs}', 'exec', '--ignore-user-config', '--ignore-rules', '--ephemeral', '--skip-git-repo-check', 'resume', '{sessionId}', '--json', '--color', 'never', '-C', '{workspacePath}', '-'],
argvTemplate: ['{accessArgs}', 'exec', '--ignore-user-config', '--ignore-rules', '--skip-git-repo-check', '--json', '--color', 'never', '-C', '{workspacePath}', '-'],
resumeArgvTemplate: ['{accessArgs}', 'exec', '--ignore-user-config', '--ignore-rules', '--skip-git-repo-check', '--color', 'never', '-C', '{workspacePath}', 'resume', '--json', '{sessionId}', '-'],
accessArgs: {
'read-only': ['--ask-for-approval', 'never', '--sandbox', 'read-only'],
'workspace-write': ['--ask-for-approval', 'never', '--sandbox', 'workspace-write'],
@@ -156,18 +164,23 @@ export const BUILTIN_TOOL_MANIFESTS: readonly ToolManifest[] = [
capabilities: { interactiveLaunch: true, headlessTask: false, structuredProgress: false, resumable: false, liveWaggleDance: false },
},
{
id: 'hermes', displayName: 'Hermes Agent', launchable: true, hookCapable: true,
hookPointer: '.hermes/hive-mind-install.json', detect: { kind: 'path', binaryName: 'hermes' }, builtin: true,
id: 'hermes', displayName: 'Hermes Agent CLI', launchable: true, hookCapable: true,
hookRoot: 'hermes-home', hookPointer: 'hive-mind-install.json', detect: { kind: 'path', binaryName: 'hermes' }, builtin: true,
capabilities: { interactiveLaunch: true, headlessTask: true, structuredProgress: false, resumable: true, liveWaggleDance: false },
task: {
argvTemplate: ['chat', '-q', '{prompt}', '-Q', '--source', 'tool', '--ignore-rules', '--max-turns', '12', '--checkpoints'],
resumeArgvTemplate: ['chat', '--resume', '{sessionId}', '-q', '{prompt}', '-Q', '--source', 'tool', '--ignore-rules', '--max-turns', '12', '--checkpoints'],
resumeArgvTemplate: ['chat', '--resume', '{sessionId}', '--no-restore-cwd', '-q', '{prompt}', '-Q', '--source', 'tool', '--ignore-rules', '--max-turns', '12', '--checkpoints'],
accessArgs: { native: [] }, promptTransport: 'arg', outputDialect: 'hermes-text', workspaceBinding: 'cwd',
permissionModes: ['native'], resumable: true,
},
},
{
id: 'openclaw', displayName: 'OpenClaw', launchable: true, hookCapable: true,
id: 'hermes-desktop', displayName: 'Hermes Desktop', launchable: true, hookCapable: false,
hookPointer: '', detect: { kind: 'candidates' }, builtin: true,
capabilities: { interactiveLaunch: true, headlessTask: false, structuredProgress: false, resumable: false, liveWaggleDance: false },
},
{
id: 'openclaw', displayName: 'OpenClaw', releaseStatus: 'roadmap', launchable: false, hookCapable: false,
hookPointer: '.openclaw/hive-mind-install.json', detect: { kind: 'path', binaryName: 'openclaw' }, builtin: true,
capabilities: { interactiveLaunch: true, headlessTask: true, structuredProgress: true, resumable: true, liveWaggleDance: false },
task: {
@@ -182,11 +195,8 @@ export const BUILTIN_TOOL_MANIFESTS: readonly ToolManifest[] = [
/**
* Tools the launcher dock + hook installer support end-to-end.
*
* Phase 1 shipped with 3 entries (Claude Code, Cursor, Claude
* Desktop — D3). Phase 4 extends to all 7 because (a) each tool
* already has a published hook-installer package
* (@waggle/hive-mind-hooks-<id>), and (b) the marginal cost per
* additional detector is one PATH lookup or candidate-path entry.
* Registered integrations can remain detectable for compatibility while
* `releaseStatus: 'roadmap'` keeps them out of both launch and hook cohorts.
*/
export const LAUNCH_COHORT: readonly ToolId[] =
BUILTIN_TOOL_MANIFESTS.filter((m) => m.launchable).map((m) => m.id as ToolId);
@@ -215,9 +225,9 @@ export const TOOL_DISPLAY_NAMES = Object.fromEntries(
* - `installed` : true iff the binary was found at a known path.
* - `installedPath` : absolute path to the binary, or null.
* - `version` : version string (best-effort; null if exec failed).
* - `hooksInstalled` : true iff a hive-mind hook pointer file
* was found AND its referenced backup file still exists
* (so a partially-rolled-back install reports false).
* - `hooksInstalled` : true iff the hook pointer and its referenced rollback
* state are healthy; tools with active-config verification must also still
* contain their marker-tagged hook entries.
* - `hookPointerPath` : the pointer file we read (or attempted),
* for diagnostics.
* - `diagnostic` : optional human-readable reason for any
@@ -228,11 +238,13 @@ export interface DetectedTool {
/** Tool id — a built-in ToolId or a loaded third-party adapter id (#5). */
id: string;
displayName: string;
/** Release qualification inherited from the manifest. Omitted means supported. */
releaseStatus?: ToolReleaseStatus;
/** True when the tool manifest allows launching from the dock. */
launchable?: boolean;
/** True when the tool manifest declares hook support. */
hookCapable?: boolean;
/** True for the built-in seven tools; false for loaded adapters. */
/** True for built-in execution surfaces; false for loaded adapters. */
builtin?: boolean;
/** True when the manifest can accept the launch prompt inline. */
acceptsInlinePrompt?: boolean;

View File

@@ -8,24 +8,67 @@ describe('BUILTIN_TOOL_MANIFESTS', () => {
it('has one manifest per supported tool, ids matching SUPPORTED_TOOLS', () => {
expect(BUILTIN_TOOL_MANIFESTS.map((m) => m.id).sort()).toEqual([...SUPPORTED_TOOLS].sort());
});
it('marks every built-in as builtin:true and launchable', () => {
it('marks every registered surface as built in', () => {
for (const m of BUILTIN_TOOL_MANIFESTS) {
expect(m.builtin).toBe(true);
expect(m.launchable).toBe(true);
}
});
it('every built-in is hook-capable', () => {
expect(BUILTIN_TOOL_MANIFESTS.filter((m) => !m.hookCapable)).toEqual([]);
it('keeps roadmap integrations detectable but outside launch and hook cohorts', () => {
const roadmap = BUILTIN_TOOL_MANIFESTS.filter((m) => m.releaseStatus === 'roadmap');
expect(roadmap.map((m) => m.id)).toEqual(['cursor', 'openclaw']);
for (const manifest of roadmap) {
expect(manifest.launchable).toBe(false);
expect(manifest.hookCapable).toBe(false);
}
});
it('derives TOOL_DISPLAY_NAMES + LAUNCH_COHORT from the manifests (unchanged values)', () => {
it('keeps desktop-only and roadmap surfaces out of the hook cohort', () => {
expect(BUILTIN_TOOL_MANIFESTS.filter((m) => !m.hookCapable).map((m) => m.id)).toEqual([
'cursor',
'hermes-desktop',
'openclaw',
]);
});
it('derives TOOL_DISPLAY_NAMES and the release-qualified launch cohort from manifests', () => {
expect(TOOL_DISPLAY_NAMES['claude-code']).toBe('Claude Code');
expect(TOOL_DISPLAY_NAMES['codex']).toBe('Codex CLI');
expect([...LAUNCH_COHORT].sort()).toEqual([...SUPPORTED_TOOLS].sort());
expect(TOOL_DISPLAY_NAMES['hermes-desktop']).toBe('Hermes Desktop');
expect([...LAUNCH_COHORT].sort()).toEqual(
SUPPORTED_TOOLS.filter((id) => id !== 'cursor' && id !== 'openclaw').sort(),
);
});
it('models Hermes CLI and Desktop as separate execution surfaces', () => {
const cli = BUILTIN_TOOL_MANIFESTS.find((m) => m.id === 'hermes')!;
const desktop = BUILTIN_TOOL_MANIFESTS.find((m) => m.id === 'hermes-desktop')!;
expect(cli).toMatchObject({
displayName: 'Hermes Agent CLI',
hookCapable: true,
hookRoot: 'hermes-home',
hookPointer: 'hive-mind-install.json',
capabilities: { interactiveLaunch: true, headlessTask: true },
});
expect(desktop).toMatchObject({
displayName: 'Hermes Desktop',
hookCapable: false,
detect: { kind: 'candidates' },
capabilities: { interactiveLaunch: true, headlessTask: false, resumable: false },
});
expect(desktop.task).toBeUndefined();
});
it('claude-code detects by PATH binary "claude" (not its id)', () => {
const cc = BUILTIN_TOOL_MANIFESTS.find((m) => m.id === 'claude-code')!;
expect(cc.detect).toEqual({ kind: 'path', binaryName: 'claude' });
});
it('keeps resumable Codex tasks persistent instead of ephemeral', () => {
const codex = BUILTIN_TOOL_MANIFESTS.find((m) => m.id === 'codex')!;
expect(codex.task?.resumable).toBe(true);
expect(codex.task?.argvTemplate).not.toContain('--ephemeral');
expect(codex.task?.resumeArgvTemplate).not.toContain('--ephemeral');
expect(codex.task?.resumeArgvTemplate).toContain('{sessionId}');
});
});
describe('applyPromptArgTemplate (#5 fast-follow)', () => {