@automagik/genie 4.260505.1 → 4.260505.2
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/dist/genie.js
CHANGED
|
@@ -2141,7 +2141,7 @@ Agent "${ctx.workerId}" SDK session ended.`),ctx.workerId}async function launchI
|
|
|
2141
2141
|
Agent "${ctx.workerId}" session ended.`),process.exit(result2.status??0)}async function findDeadResumable(team,role,isAliveFn=isPaneAliveOrDead){let prefiltered=(await list()).filter((w)=>w.role===role&&w.team===team&&w.provider==="claude"&&w.transport==="tmux"),candidate=null;for(let w of prefiltered)if((await getCurrentExecutor(w.id))?.claudeSessionId){candidate=w;break}if(!candidate)return null;if(await isAliveFn(candidate.paneId)){if(candidate.kind==="permanent")throw new OwnerSpawnCollisionError(role,team,candidate.id,candidate.paneId,candidate.state);return null}return candidate}async function rejectDuplicateRole(team,role){let existing=await list();for(let w of existing){if(w.team!==team)continue;let matchesRole=w.role===role,matchesName=w.id===role;if(!matchesRole&&!matchesName)continue;if(await classifyCollisionLiveness(w)==="alive"){let collisionLabel=matchesName&&!matchesRole?`name "${w.id}"`:`role "${role}"`;console.error(`Error: Worker with ${collisionLabel} already exists in team "${team}" (state: ${w.state}, pane: ${w.paneId})
|
|
2142
2142
|
Use a different --role name for a second worker, e.g.: --role ${role}-2`),process.exit(1)}await unregister(w.id)}}async function classifyCollisionLiveness(w){if(!await resolveWorkerLivenessByTransport(w))return"dead";if(w.session&&/^%\d+$/.test(w.paneId)){if(await getPaneSession(w.paneId)!==w.session)return"recycled"}return"alive"}async function getPaneSession(paneId){try{return(await executeTmux2(`display-message -t '${paneId}' -p '#{session_name}'`)).trim()||null}catch{return null}}async function resolveNativeTeam(team,_repoPath,options){let leaderName=await resolveLeaderName(team),sanitizedTeam=sanitizeTeamName(team),leaderAgent=await getAgentByName(leaderName,sanitizedTeam).catch(()=>null),parentSessionId;if(leaderAgent&&!options.isIdentitySpawn)parentSessionId=(await shouldResume(leaderAgent.id).catch(()=>null))?.sessionId;if(!parentSessionId)parentSessionId=await discoverClaudeParentSessionId()??`genie-${team}`;await ensureNativeTeam(team,`Genie team: ${team}`,parentSessionId);let spawnColor=options.color??await assignColor(team),nativeTeam;if(options.provider==="claude")nativeTeam={enabled:!0,parentSessionId,color:spawnColor,agentType:options.role??"general-purpose",planModeRequired:options.planMode,permissionMode:options.permissionMode,agentName:options.role};return{parentSessionId,spawnColor,nativeTeam}}function autoSyncDisabled(options){return options.noAutoSync===!0||options.autoSync===!1||process.env.GENIE_DISABLE_AUTO_SYNC==="1"}function isWithinAgentsRoot(agentsRoot,agentDir){let rel=relative(agentsRoot,agentDir);return rel!==""&&!rel.startsWith("..")&&!isAbsolute2(rel)}async function tryAutoRegisterAgent(name,options){if(autoSyncDisabled(options))return null;let workspace=_spawnAutoSyncDeps.findWorkspace();if(!workspace)return null;let agentsRoot=join50(workspace.root,"agents"),agentDir=resolvePath(agentsRoot,name);if(!isWithinAgentsRoot(agentsRoot,agentDir))return null;let agentsMdPath=join50(agentDir,"AGENTS.md");if(!_spawnAutoSyncDeps.existsSync(agentsMdPath))return null;let frontmatter;try{frontmatter=_spawnAutoSyncDeps.parseFrontmatter(_spawnAutoSyncDeps.readFileSync(agentsMdPath,"utf-8"))}catch{return null}if(frontmatter.name!==name)return null;if(typeof frontmatter.description!=="string"||frontmatter.description.trim().length===0)return null;try{if(await _spawnAutoSyncDeps.syncSingleAgentByName(workspace.root,name)==="not-found")return null}catch{return null}let resolved=await _spawnAutoSyncDeps.resolveAgent(name);if(!resolved)return null;return _spawnAutoSyncDeps.stderr(`Auto-registered agent '${name}' from ${agentDir}`),resolved}async function resolveAgentForSpawn(name,options){let resolved=await _spawnAutoSyncDeps.resolveAgent(name)??await tryAutoRegisterAgent(name,options);if(!resolved)console.error(`Error: Agent "${name}" not found in directory or built-ins.`),console.error(` Register with: genie dir add ${name} --dir <path>`),console.error(" Or use a built-in: engineer, reviewer, qa, fix, ..."),process.exit(1);let entry2=resolved.entry,identityPath=null;if(resolved.builtin)identityPath=resolveBuiltinAgentPath(name);else if(entry2.dir)identityPath=_spawnAutoSyncDeps.loadIdentity(entry2);if(!identityPath)identityPath=resolveBuiltinAgentPath(name);let repoPath=resolveAgentWorkingDir(entry2,options.cwd),model=options.model;if(!model){let ctx=buildSpawnResolveContext(name,entry2);model=resolveField(entry2,"model",ctx)}return{entry:entry2,repoPath,identityPath,model}}function buildSpawnResolveContext(agentName,_entry){let ctx={};try{let ws=findWorkspace();if(ws){let wsConfig=getWorkspaceConfig(ws.root);ctx.workspaceDefaults=wsConfig.agents?.defaults}}catch{}if(agentName.includes("/")){let parentName=agentName.split("/")[0];try{let{readFileSync:readFileSync28,existsSync:existsSync43}=__require("fs"),{join:join51}=__require("path"),ws=findWorkspace();if(ws){let parentAgentsMd=join51(ws.root,"agents",parentName,"AGENTS.md");if(existsSync43(parentAgentsMd)){let{parseFrontmatter:parseFrontmatter3}=(init_frontmatter(),__toCommonJS(exports_frontmatter)),parentFm=parseFrontmatter3(readFileSync28(parentAgentsMd,"utf-8"));ctx.parent={name:parentName,fields:parentFm}}}}catch{}}return ctx}function resolveAgentWorkingDir(entry2,explicitCwd){if(explicitCwd)return explicitCwd;if(entry2.dir)return entry2.dir;let repo=entry2.repo;if(repo&&__require("fs").existsSync(repo))return repo;return process.cwd()}function buildDirectoryPermissionSpawnParams(entry2){let permissions=entry2.permissions?.allow?.length||entry2.permissions?.deny?.length?{allow:entry2.permissions.allow,deny:entry2.permissions.deny}:void 0;return{...permissions?{permissions}:{},...entry2.disallowedTools?{disallowedTools:entry2.disallowedTools}:{}}}async function buildSpawnParams2(name,team,options,agent,preassignedSessionId,agentTemplate){let resolvedProvider=options.provider??agent.entry.provider??"claude",params={provider:resolvedProvider,team,role:name,agentTemplate:agentTemplate??name,skill:options.skill,extraArgs:options.extraArgs,model:agent.model,systemPromptFile:agent.identityPath??void 0,promptMode:agent.entry.promptMode,initialPrompt:options.prompt??options.initialPrompt,newWindow:options.newWindow,windowTarget:options.window,...buildDirectoryPermissionSpawnParams(agent.entry)},isIdentitySpawn=options.role!==void 0&&options.role!==name,{parentSessionId,spawnColor,nativeTeam}=await resolveNativeTeam(team,agent.repoPath,{...options,provider:resolvedProvider,role:name,isIdentitySpawn});if(nativeTeam)params.nativeTeam=nativeTeam;try{let{injectTeamHooks:injectTeamHooks2}=await Promise.resolve().then(() => (init_inject(),exports_inject));if(await injectTeamHooks2(team))console.log(` Hooks: injected genie hook dispatch into team "${team}"`)}catch(err){console.warn(`Warning: could not inject hooks for team "${team}": ${err instanceof Error?err.message:err}`)}if(params.provider==="codex")try{let{injectCodexHooks:injectCodexHooks2}=await Promise.resolve().then(() => (init_codex_inject(),exports_codex_inject));if(await injectCodexHooks2())console.log(" Hooks: injected genie hook dispatch into ~/.codex/config.toml")}catch(err){console.warn(`Warning: could not inject codex hooks: ${err instanceof Error?err.message:err}`)}if(params.provider==="claude")params.sessionId=preassignedSessionId??crypto.randomUUID();if(params.provider==="claude"){if(await startOtelReceiver())params.otelPort=getOtelPort(),params.otelLogPrompts=!0}return{params,parentSessionId,spawnColor}}async function maybeStartOtelRelay(nt,validated,insideTmux){if(!nt?.enabled&&validated.provider==="codex"&&insideTmux)return ensureCodexOtelConfig(),await ensureOtelRelay(validated.team);return!1}function buildSdkRuntimeExtra(options){let extra={};if(options.sdkMaxTurns!=null)extra.maxTurns=options.sdkMaxTurns;if(options.sdkMaxBudget!=null)extra.maxBudgetUsd=options.sdkMaxBudget;if(options.sdkEffort)extra.effort=options.sdkEffort;if(options.sdkResume)extra.resume=options.sdkResume;return Object.keys(extra).length>0?extra:void 0}async function dispatchSpawn(ctx,validated,options,agent,insideTmux){if(validated.provider==="claude-sdk"){let streamFormat=options.streamFormat??"text",streamOpts=options.stream||options.sdkStream?{stream:!0,streamFormat}:void 0;return await launchSdkSpawn(ctx,agent.entry.permissions,streamOpts,agent.entry.sdk,buildSdkRuntimeExtra(options))}if(insideTmux)return await launchTmuxSpawn(ctx);return await launchInlineSpawn(ctx)}async function resolveTeamName3(opts){if(opts.explicitTeam)return opts.explicitTeam;if(opts.entryTeam)return opts.entryTeam;let env=opts.env??process.env;if(env.GENIE_TEAM)return env.GENIE_TEAM;return await(opts.discover??discoverTeamName)()??null}async function pickParallelShortId(baseName,team,uuid){if(!UUID_REGEX.test(uuid))throw Error(`pickParallelShortId: expected a well-formed UUID (8-4-4-4-12 hex), got ${JSON.stringify(uuid)}`);let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db)),sql=await getConnection2();for(let k=4;k<=uuid.length;k++){let slice=uuid.slice(0,k),id=`${baseName}-${slice}`;if((await sql`
|
|
2143
2143
|
SELECT id FROM agents WHERE id = ${id} LIMIT 1
|
|
2144
|
-
`).length===0)return slice}return uuid}async function resolveSpawnIdentity(name,team,uuidFactory=()=>crypto.randomUUID(),isAliveFn=(agent)=>resolveWorkerLivenessByTransport(agent)){if(!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(name))return{kind:"canonical",workerId:
|
|
2144
|
+
`).length===0)return slice}return uuid}async function resolveSpawnIdentity(name,team,uuidFactory=()=>crypto.randomUUID(),isAliveFn=(agent)=>resolveWorkerLivenessByTransport(agent)){if(!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(name))return{kind:"canonical",workerId:name,sessionUuid:uuidFactory()};let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db)),rows=await(await getConnection2())`
|
|
2145
2145
|
SELECT id, pane_id, team FROM agents WHERE id = ${name} LIMIT 1
|
|
2146
2146
|
`;if(rows.length===0)return{kind:"canonical",workerId:name,sessionUuid:uuidFactory()};let existing=rows[0];if(existing.team!==null&&existing.team!==team){let sessionUuid2=uuidFactory(),shortId2=await pickParallelShortId(name,team,sessionUuid2);return{kind:"parallel",workerId:`${name}-${shortId2}`,sessionUuid:sessionUuid2,canonicalId:name}}let alive=!1;if(existing.pane_id)try{alive=await isAliveFn({id:existing.id,paneId:existing.pane_id})}catch(err){let message=err instanceof Error?err.message:String(err);if(err instanceof TmuxUnreachableError||message.includes("no server running")||message.includes("server exited")||message.includes("error connecting"))alive=!1;else throw err}if(!alive)return{kind:"canonical",workerId:name,sessionUuid:uuidFactory()};let sessionUuid=uuidFactory(),shortId=await pickParallelShortId(name,team,sessionUuid);return{kind:"parallel",workerId:`${name}-${shortId}`,sessionUuid,canonicalId:name}}async function resolveTeamAndResume(effectiveRole,options,agent){let teamWasExplicit=Boolean(options.team),team=await resolveTeamName3({explicitTeam:options.team,entryTeam:agent.entry?.team});if(!team){let candidates=await findTeamsContainingAgent(effectiveRole);if(candidates.length===1)team=candidates[0];else if(candidates.length>1)return console.error(`Error: agent "${effectiveRole}" is a member of multiple teams (${candidates.join(", ")}). Pass --team <name> to disambiguate.`),process.exit(1)}if(!team){if(await get2(effectiveRole))team=sanitizeTeamName(effectiveRole)}if(!team)return console.error(`Error: --team is required for agent "${effectiveRole}" (or set GENIE_TEAM, run inside a genie session, or register the agent in a team config).`),process.exit(1);let deadResumable=await findDeadResumable(team,effectiveRole);if(deadResumable){let executor=await getCurrentExecutor(deadResumable.id),sessionShort=executor?.claudeSessionId?.slice(0,8)??"unknown";console.log(`Resuming existing session for "${effectiveRole}" (session: ${sessionShort}...)`);try{return await resumeAgent(deadResumable),{team,teamWasExplicit,resumed:deadResumable.id}}catch(err){if(err instanceof ResumePaneVanishedError){if(console.warn(`\u26A0 Resume of "${effectiveRole}" produced a dead pane (${err.reason}). Clearing stale executor and falling through to fresh spawn.`),executor?.id)await updateExecutorState(executor.id,"terminated").catch(()=>{});return{team,teamWasExplicit}}throw err}}return{team,teamWasExplicit}}async function resolveTeamAndResumeOrExit(effectiveRole,options,agent){try{return await resolveTeamAndResume(effectiveRole,options,agent)}catch(err){if(err instanceof OwnerSpawnCollisionError)return console.error(`Error: owner agent "${err.ownerName}" already alive in team "${err.team}" (id: ${err.conflictId}, pane: ${err.conflictPaneId}, state: ${err.conflictState}).
|
|
2147
2147
|
Owner identities are exclusive \u2014 to spawn a separate worker under the same template, use --role <custom-name>:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automagik/genie",
|
|
3
|
-
"version": "4.260505.
|
|
3
|
+
"version": "4.260505.2",
|
|
4
4
|
"description": "Collaborative terminal toolkit for human + AI workflows. NOTE: the npm distribution is being soft-deprecated — the canonical install is `curl -fsSL https://get.automagik.dev/genie | bash` (cosign + SLSA verified). See https://automagik.dev/genie/security/distribution-sovereignty",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genie",
|
|
3
|
-
"version": "4.260505.
|
|
3
|
+
"version": "4.260505.2",
|
|
4
4
|
"description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, turn them into wishes, execute with /work, validate with /review, and ship as one team.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Namastex Labs"
|
package/skills/report/SKILL.md
CHANGED
|
@@ -201,7 +201,7 @@ Report orchestrates multiple tools but must **never modify source code** — inv
|
|
|
201
201
|
|
|
202
202
|
```bash
|
|
203
203
|
# Spawn a tracer subagent for investigation
|
|
204
|
-
genie agent spawn
|
|
204
|
+
genie agent spawn trace
|
|
205
205
|
```
|
|
206
206
|
|
|
207
207
|
Browser dispatch uses direct `agent-browser` commands alongside the trace subagent.
|
|
@@ -240,7 +240,7 @@ The report agent:
|
|
|
240
240
|
# Agent asks: "What did you see?" → "Engineers show welcome screen but empty prompt"
|
|
241
241
|
|
|
242
242
|
# 2. Run /trace
|
|
243
|
-
genie agent spawn
|
|
243
|
+
genie agent spawn trace
|
|
244
244
|
genie agent send 'Trace: genie work dispatches engineers but they start idle. Check dispatch.ts and protocol-router.ts.' --to tracer
|
|
245
245
|
# Wait for diagnosis...
|
|
246
246
|
|
package/skills/trace/SKILL.md
CHANGED
|
@@ -39,7 +39,7 @@ Trace must run in **isolation** — the subagent must not modify any source file
|
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
41
|
# Spawn a tracer subagent (read-only investigation)
|
|
42
|
-
genie agent spawn
|
|
42
|
+
genie agent spawn trace
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
## Task Lifecycle Integration (v4)
|
|
@@ -63,7 +63,7 @@ An engineer reports that `genie work` dispatches engineers but they sit idle. Th
|
|
|
63
63
|
|
|
64
64
|
```bash
|
|
65
65
|
# 1. Spawn a tracer (read-only — no code changes)
|
|
66
|
-
genie agent spawn
|
|
66
|
+
genie agent spawn trace
|
|
67
67
|
|
|
68
68
|
# 2. Send the symptoms
|
|
69
69
|
genie agent send 'Trace: genie work dispatches engineers but they start idle at the prompt. No task received. genie wish status shows in_progress but nothing happens. Check dispatch.ts workDispatchCommand and protocol-router.ts sendMessage.' --to tracer
|