@anhlp/alp 0.0.0-beta.2 → 0.0.1-beta.3
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 +5 -5
- package/dist/acp.js +104 -21
- package/dist/alpd.js +705 -326
- package/docs/modular-monorepo.md +1 -1
- package/package.json +4 -4
- package/packages/cli/dist/acp/main.js +1 -1
- package/packages/cli/dist/acp.js +104 -21
- package/packages/cli/dist/alpd.js +705 -326
- package/packages/cli/dist/backup.js +1 -1
- package/packages/cli/dist/build-manifest.json +1 -1
- package/packages/cli/dist/cli.js +6 -2
- package/packages/cli/dist/daemon.js +6 -2
- package/packages/client/dist/index.d.ts +16 -1
- package/packages/client/dist/index.js +83 -5
- package/packages/client/dist/supervise.js +18 -12
- package/packages/core/dist/library-edit.d.ts +7 -4
- package/packages/core/dist/library-edit.js +59 -6
package/dist/alpd.js
CHANGED
|
@@ -7805,7 +7805,7 @@ var require_content_type = __commonJS({
|
|
|
7805
7805
|
// packages/daemon/src/main.ts
|
|
7806
7806
|
import { spawn as spawn9 } from "node:child_process";
|
|
7807
7807
|
import { openSync, writeSync } from "node:fs";
|
|
7808
|
-
import { mkdir as mkdir18, open as open7, readFile as readFile24, rename as
|
|
7808
|
+
import { mkdir as mkdir18, open as open7, readFile as readFile24, rename as rename10, stat as stat13, unlink as unlink6, utimes as utimes3, writeFile as writeFile18 } from "node:fs/promises";
|
|
7809
7809
|
import path40 from "node:path";
|
|
7810
7810
|
import { fileURLToPath } from "node:url";
|
|
7811
7811
|
|
|
@@ -7813,8 +7813,8 @@ import { fileURLToPath } from "node:url";
|
|
|
7813
7813
|
var templates_default = { "ALP.md": "# ALP project instructions\n\n## Working relationship\n\nThe user communicates with main. The team is fixed for each session:\n\n- Ph\u1EDF (default): main owns delivery and technical execution; it implements\n directly or delegates bounded work to peer. There is no separate lead.\n- Cafe: main owns the outcome and supervises lead; lead implements or\n delegates to peer. Main routes implementation changes through lead.\n\nIn both, main runs by default on Opus 5.5 with high effort and full access,\nand starts a supervisor that reviews the process after each of main's turns.\nThe supervisor asks main about mistakes; main answers and records lessons it\nfollows later.\n\nOracle provides read-only advice through the configured model lenses; reviewer independently\nreviews one diff. Both return once only to the requesting coordinator. They do\nnot spawn agents. Default maximum concurrent peers is two; raise the configured\nlimit only at the user's request. Other model and effort choices belong to the\ntechnical coordinator. A role grants no additional filesystem permissions,\ncredentials, or runtime tools.\n\n## Shared work contract\n\n- Read project instructions and the selected agent's AGENT.md. Load skills only\n when needed; do not assume that skills or tools from another runtime exist.\n- Each assignment states its objective, root, owned/excluded paths, constraints\n and their sources, verification, resources, owner, and expected handoff.\n- Keep a single writer per shared checkout. Transfer ownership before main or lead\n intervenes in a peer's files. Parallel writers need isolated workspaces and\n explicit shared-interface agreements.\n- Return artifacts and real verification evidence, with remaining risks. A worker's\n completion message is a candidate, not an independent acceptance verdict.\n- Review should be independent of authorship: the coordinator reviews peer work;\n in Cafe, main reviews lead work. Use reviewer for logic changes and risky\n changes, including main-authored changes. Disclose unavailable independent review\n rather than claiming it occurred.\n- Changes to scope or priorities flow through the technical coordinator's current plan\n and the affected peer's revised brief. Direct questions can receive direct answers;\n an exceptional intervention must be reconciled with the same shared state.\n- Tasks live in `.alp/tasks` and are shared with the user. Only the user and main\n create or change them; other agents read them and report work they find to their\n requester.\n- Use only actual runtime delegation/messaging capabilities. Without them, explain\n the limitation, perform authorized work directly where possible, and never claim\n that a lead or peer session ran or reviewed anything.\n\n## Project-specific constraints\n\nAdd the project's outcome, boundaries, verification commands, and authorization\nrequirements here. User instructions take precedence over this starter workflow.\n", "role-skills.json": '{\n "main": ["goal-griller", "xia", "sequence-execution-plan", "prompt-leverage", "bug-loop", "smart-commits"],\n "lead": ["xia", "sequence-execution-plan", "prompt-leverage", "bug-loop", "smart-commits"],\n "peer": ["xia", "bug-loop", "smart-commits"],\n "oracle": [],\n "reviewer": [],\n "supervisor": []\n}\n', "teams/pho/HOUSE_RULES.md": "In Ph\u1EDF (pho), main implements or directly delegates to peer; do not create lead. In Cafe (cafe), main supervises lead; lead may implement or delegate to peer. The technical coordinator chooses each peer's model and effort. Use oracle for significant uncertainty; use reviewer for logic changes and risky changes, not mandatory for typo/format fixes. Advisors return only to their requesting coordinator.\n", "teams/pho/team.json": '{\n "label": "Ph\u1EDF",\n "description": "Main implements or delegates to peer; a supervisor reviews the process.",\n "main": "main",\n "members": {\n "main": { "model": "claude:claude-opus-5-5", "thinking": "high" },\n "peer": { "role": "peer" },\n "oracle": { "role": "advisor" },\n "reviewer": { "role": "reviewer" }\n },\n "delegation": { "main": ["peer", "oracle", "reviewer"] },\n "maxPeers": 2,\n "supervisor": { "agent": "supervisor", "model": "claude:claude-sonnet-5", "thinking": "medium" }\n}\n', "teams/cafe/HOUSE_RULES.md": "In Ph\u1EDF (pho), main implements or directly delegates to peer; do not create lead. In Cafe (cafe), main supervises lead; lead may implement or delegate to peer. The technical coordinator chooses each peer's model and effort. Use oracle for significant uncertainty; use reviewer for logic changes and risky changes, not mandatory for typo/format fixes. Advisors return only to their requesting coordinator.\n", "teams/cafe/team.json": '{\n "label": "Cafe",\n "description": "Main supervises lead, who implements or delegates to peer; a supervisor reviews the process.",\n "main": "main",\n "members": {\n "main": { "model": "claude:claude-opus-5-5", "thinking": "high" },\n "lead": { "role": "lead" },\n "peer": { "role": "peer" },\n "oracle": { "role": "advisor" },\n "reviewer": { "role": "reviewer" }\n },\n "delegation": { "main": ["lead", "oracle", "reviewer"], "lead": ["peer", "oracle", "reviewer"] },\n "maxPeers": 2,\n "supervisor": { "agent": "supervisor", "model": "claude:claude-sonnet-5", "thinking": "medium" }\n}\n', "skills/xia/SKILL.md": "---\nname: xia\ndescription: Research an unfamiliar implementation, reuse opportunity, or version-sensitive integration using local artifacts and primary sources. Return an evidence-backed recommendation before implementation; do not expand the assigned scope.\n---\n\n# Research before building\n\nStart with a named question that affects a decision. Use a short local inspection\nwhen the relevant seam is clear; broaden to upstream sources only when the question\nrequires it. Research is read-only with respect to project artifacts. Temporary\nexperiments require an available isolated scratch location and appropriate authority.\n\n1. Establish the actual stack from manifests, lockfiles, configuration, and commands.\n Read ALP.md and relevant project guidance. Do not infer a stack from folder names.\n2. Search nearby code, tests, configuration, scripts, and docs for existing behavior\n and reusable parts. Record the scope searched before saying something is absent.\n3. For external behavior, inspect primary upstream source/examples and official\n documentation matching the installed version. Distinguish current guidance from\n the version this project actually uses.\n4. Compare viable approaches only where the choice matters. Prefer an existing\n local capability, then a supported dependency capability, then a small adaptation.\n Explain what evidence would change the recommendation.\n\nLabel significant findings by evidence type:\n\n- Local: file path, command output, test, or repository revision.\n- Upstream: repository URL and revision when available.\n- Docs: official source and applicable version.\n- Inference: a conclusion drawn from the cited evidence, with uncertainty stated.\n\nIf browsing is unavailable, return the local findings and the unresolved external\nquestion. Do not invent a source or claim version compatibility without evidence.\nResearch does not authorize installing dependencies, editing code, or deploying.\n\nReturn the question, findings/sources, reuse candidates, recommendation, alternatives\nif material, unknowns, and the next decision. Include actual inspection commands and\nresults in the handoff. A peer returns this to lead; lead returns unresolved scope\ndecisions to main. Main can decide technical matters within the user's authorization.\n\nUse available synchronous delegation for a separate research assignment only when\nhelpful. Do not assume a Scout role or parallel session exists; research is a peer\ndisposition. An agent can inspect evidence itself without spawning another agent.\n", "skills/smart-commits/SKILL.md": "---\nname: smart-commits\ndescription: Package existing owned changes into focused local Git commits with verification evidence. Use when commits are requested or part of the assigned handoff; do not activate merely because a file was edited.\n---\n\n# Package the authorized changes\n\nConfirm this is a Git repository and that local commits belong to the requested\ndeliverable. A non-Git task returns file artifacts; do not initialize Git merely to\nproduce a SHA. A read-only task cannot commit. Existing user authorization remains\nvalid; the skill adds no confirmation gate or permission to push.\n\nInspect the root, branch, status, unresolved merges, staged paths, and staged/unstaged\ndiffs with Git commands. Verify any supplied base revision. Preserve work outside\nthe assignment. If unrelated staged changes would enter the commit, stop packaging\nand report the conflict without resetting or unstaging someone else's work.\n\nGroup changes by behavior and intent. Keep implementation and its direct tests\ntogether where useful. Read each included diff and stage only the owned paths or\nhunks. Avoid whole-tree staging in a shared checkout. Do not add unrelated fixes or\nformatting to make a commit easier.\n\nRun the relevant checks from the project/brief, reusing recent results when still\napplicable. State failures and omissions accurately. If hooks may cause external\nside effects, inspect their configured behavior before invoking them and stay within\nthe task's authorization. Do not bypass hooks or protections to get a green commit.\n\nCreate focused messages explaining the change and its purpose, following the\nrepository's convention. Check commit success before reading the candidate SHA.\nVerify that the candidate contains only the intended scope and that the base\nrelationship is valid when a base was supplied. A failed commit produces no new\ncandidate, and an already-clean tree needs no empty commit.\n\nDo not rewrite a candidate already handed off for review. Corrections should be\nnew commits unless rewriting was specifically agreed. Push requires existing explicit\nauthorization for the intended remote/branch; a configured remote does not provide it.\n\nReturn root, branch, actual base/candidate SHAs, commit summary, owned scope, checks\nand results, remaining work, and push status. Lead/main still review the artifact;\nthe writer does not self-accept its work.\n", "skills/sequence-execution-plan/SKILL.md": "---\nname: sequence-execution-plan\ndescription: Order multiple ALP work items by real dependencies, verification, and writer ownership. Use when Main or Lead coordinates several outcomes or must replan after a blocker, changed premise, or rejection.\n---\n\n# Order executable work\n\nUse the task contract and available evidence. Planning authority comes from the\ncurrent assignment: main coordinates the overall outcome; lead plans technical\nexecution within its assigned scope. A peer reports dependencies instead of\nreassigning ownership or widening the plan.\n\nSeparate urgency from dependency order. A lower-priority prerequisite may need to\nrun first, but it does not close the higher-priority outcome. A mitigation reduces\ncurrent impact; it is not proof of a durable fix.\n\nBuild small work items that each deliver observable progress. Keep implementation\nand its direct verification together. Split when an unresolved decision blocks one\npart, separate outcomes need independent acceptance, or a resource/human wait would\nstall unrelated work. Do not impose a fixed task count on a simple change.\n\nFor each item identify the result, owner, owned/excluded paths, true prerequisites,\nverification seam, completion evidence, and current state. Distinguish necessary\nfoundations from optional improvements and speculative future abstractions.\n\nArrange Now, Next, and Later using causal explanations: which prerequisite blocks\nwhich outcome, which check validates it, and which decision opens the next branch.\nRead [the plan format](references/plan-template.md) when a durable multi-item plan\nhelps. Use the project's existing plan location; do not create unrelated ignore\nrules or a new planning hierarchy just to satisfy this skill.\n\nCurrent Paseo delegation is sequential: each parent waits for one child's result.\nDo not promise parallel workers or live upstream mailboxes. A shared checkout has\none writer at a time; main intervention requires ownership to be released first.\nAllocate ports, scratch paths, and other exclusive resources if the assignment uses\nthem. Separate worktrees alone do not isolate ports or a shared database.\n\nReplan when evidence changes a premise, a dependency blocks work, the user changes\nscope, or repeated rejection reveals that the assignment needs restructuring.\nUpdate the facts and constraints before issuing a revised brief. Preserve unfinished\nacceptance criteria when merging items. Candidate remains candidate until the\nresponsible reviewer has evaluated the actual evidence.\n", "skills/sequence-execution-plan/references/plan-template.md": "# Proportional plan format\n\nUse the relevant fields; a plan is a decision aid, not a form-completion exercise.\n\n```text\nOutcome and acceptance evidence:\nUser constraints and their sources:\nCurrent facts / technical assumptions:\n\nID | Result | Owner | Owned/excluded scope | Prerequisites | Verification | State\n\nNow:\nNext (and what unlocks it):\nLater:\nWriter/resource allocation:\nOpen decisions and blockers:\nReplan reason, when applicable:\n```\n\nStates can be ready, blocked, running, candidate, accepted, or rejected. An item is\naccepted based on artifacts and verification; a successful process exit is not an\nacceptance criterion by itself. Omit branch/SHA fields in non-Git projects.\n", "skills/prompt-leverage/SKILL.md": "---\nname: prompt-leverage\ndescription: Convert a task into a self-contained delegation brief without changing user intent or predetermining the solution. Use before Main assigns Lead or Lead assigns Peer, or when asked to improve an execution prompt.\n---\n\n# Prepare an actionable brief\n\nIdentify the requested result and the recipient's responsibility. Preserve actual\nuser constraints, but distinguish them from current design choices the recipient\nmay question. Do not seed a reviewer's verdict or turn a preferred implementation\ninto an invented requirement.\n\nA useful brief supplies the root, outcome, relevant context, owned/excluded paths,\nauthority, important dependencies, resources, verification, and expected handoff.\nUse [the brief format](references/brief-template.md) for substantial assignments;\ncombine fields for a small task. A real base SHA is useful in a Git repository but\nis not required for a plain file project. Never fabricate one.\n\nVerification must describe the observable behavior and, when known, its executable\ncheck. If a command still needs discovery, state how it should be established from\nthe repository. Do not invent a test command just to fill a field.\n\nChoose the relevant installed skill by task type and mention it in the brief:\nresearch -> xia; diagnosis/regression -> bug-loop; an authorized commit handoff ->\nsmart-commits. Do not require skills absent from the recipient's package. A reviewer\ncan inspect an existing candidate without additional skill ceremonies.\n\nOn current Paseo, pass the full brief as alp_delegate.task with an authorized agent\nand optional mode. Model and reasoning inherit from the parent; there is no model\nargument on this tool. Request read-only for research/review where appropriate.\nImplementation requires workspace-write on the parent and child. The call waits\nfor a final handoff, so include everything needed to work without a live Q&A channel.\n\nThis skill grants no new authority and never bypasses the configured routing graph.\nIf asked only to improve a prompt, return the prompt. If assignment is authorized,\nsend it using the actual runtime tool after preparing it. Missing capabilities\nmust be reported, not simulated.\n", "skills/prompt-leverage/references/brief-template.md": "# Brief and handoff format\n\nUse enough detail to make an independent worker effective, without prescribing an\nunverified solution. Available context includes ALP.md and the selected agent's own\ninstructions; the parent's full transcript is not automatically available.\n\n```text\nTask and recipient:\nRoot:\nOutcome:\nContext / prior decisions:\nRequired constraints + sources:\nCurrent technical choices (open to evidence):\nOwned paths / excluded paths:\nAuthority and permission mode:\nDependencies / writer ownership / resource allocation:\nVerification commands or expected observable checks:\nRelevant installed skill, if needed:\nCandidate/base revision, if applicable:\nReturn: outcome, artifact, scope, actual checks/results, risks, ownership status.\n```\n\nFor read-only review, supply the candidate/artifact and independent acceptance\ncriteria. For a correction, include the concrete finding and prior evidence. For\na resumed assignment, include the findings needed from the previous handoff because\nthe next child is a new session. A blocked recipient should return its blocker and\ncompleted independent work rather than waiting for a nonexistent mailbox.\n", "skills/goal-griller/SKILL.md": '---\nname: goal-griller\ndescription: Turn an underspecified user request into a verifiable task contract. Use at Main intake when missing outcomes, scope, or acceptance evidence would materially change execution; skip when the request is already clear.\n---\n\n# Clarify the outcome\n\nThis skill serves the user-facing owner. In the starter workflow that is main;\nlead and peer return missing decisions to their assigning agent instead of opening\nan independent user conversation.\n\nRead the request, existing decisions, ALP.md, and the few relevant local artifacts.\nSeparate the desired result from a suggested implementation. Establish:\n\n- Outcome: the observable state the user wants.\n- Evidence: a check, artifact, or measurement showing that result.\n- Scope: what may change and what the user requires to remain unchanged.\n- Context: the sources needed to make the first decision.\n- Verification: a fast feedback loop and any final checks.\n- Completion/blockers: when the work is done and what requires a user decision.\n\nInfer routine details from the repository and existing authorization. Ask only\nquestions whose answers change scope, behavior, cost, or a consequential decision.\nBundle independent questions concisely and offer a reasonable default where useful.\nContinue independent authorized work while waiting; a required answer is not\nsupplied by elapsed time.\n\nDo not send a researcher to invent the meaning of words such as "better" or\n"production-ready" when the outcome itself needs the user\'s judgment. Conversely,\ndo not ask the user to identify a build command or file path that local evidence\ncan establish.\n\nProduce a proportional contract, for example:\n\n```text\nOutcome: observable result\nEvidence: check/artifact and expected behavior\nScope: owned paths; exclusions\nConstraints: requirement + source; distinguish technical choices\nContext: relevant artifacts\nVerification: fast check; final checks\nDone: completion condition\nBlocked if: material missing decision or prerequisite\n```\n\nFor an already-authorized task, a clear contract is enough to proceed; this skill\nadds no approval gate. If asked only to draft a contract, return it without starting\nimplementation. Route a multi-item task to sequence-execution-plan, or a single\nassignment to prompt-leverage, reading the next skill only when needed.\n', "skills/bug-loop/SKILL.md": "---\nname: bug-loop\ndescription: Diagnose a bug or regression through a reproducible feedback loop, competing explanations, and verified correction. Use for failing tests, incorrect behavior, or performance regressions; read-only assignments stop before modifying project artifacts.\n---\n\n# Establish a failing loop, then fix the cause\n\nStart from the reported behavior and the assigned scope. Find the smallest useful\ncommand, request, replay, or test that distinguishes the bug from correct behavior.\nRun it and record the actual result before claiming reproduction. For intermittent\nfailures, record conditions, seed when applicable, and observed failure frequency.\n\nIf reproduction needs unavailable credentials, hardware, data, or a user action,\nreport the specific gap to the assigning agent; main handles user communication.\nContinue useful in-scope investigation, but label an unconfirmed hypothesis as such.\nDo not claim a fix was verified without a meaningful observable check.\n\nReduce the reproducer without removing the failure. Form competing explanations\nwhere the cause is uncertain, each with an observable prediction. Probe the few\npoints that distinguish them; change one variable at a time. Prefer measured\nevidence over accumulating workarounds for the same unexplained symptom.\n\nUse isolated scratch space for disposable experiments. A read-only assignment can\ninspect and diagnose; it does not authorize instrumentation edits to project code.\nFor performance work, compare equivalent workloads and record relevant conditions\nso that a timing difference is not mistaken for a causal improvement.\n\nOnce a cause is established and writing is authorized:\n\n1. Put a regression check at the seam that reproduces the actual failure.\n2. Confirm that the check fails for the reported reason on the broken behavior.\n3. Make the smallest correction consistent with the intended contract.\n4. Run the check again, then relevant surrounding checks and the original scenario.\n5. Remove temporary instrumentation and restore any deliberate fault injection.\n\nRead [test proof](references/test-proof.md) when designing/reviewing the regression\ntest or when it passes before the fix. Do not weaken expectations, skip failing\nchecks, or mock away the behavior to manufacture a pass. A changed requirement must\nbe established independently of what the implementation currently returns.\n\nReturn the reproduction command and result, established cause or remaining\nhypotheses, changed artifacts, before/after checks, limits, and unresolved risks.\nA peer returns to lead; lead returns to main. Read-only investigation returns\ndiagnostic evidence and a proposed correction, not an imaginary code change.\n", "skills/bug-loop/references/test-proof.md": "# Evidence for a regression correction\n\nChoose an observable boundary close enough to isolate the failure but broad enough\nto exercise its real cause. A unit test of one caller cannot establish a bug that\ndepends on ordering across several callers.\n\nExpected behavior comes from the user request, a supported contract, or independently\nderived examples. Do not calculate the expected result using the implementation\nbeing tested. Mock external boundaries only where doing so leaves the claimed\nbehavior under test.\n\nDescribe proof accurately:\n\n- Green only: the current check passes; this alone does not show regression coverage.\n- Red -> green: the check fails on broken behavior and passes after the correction.\n- Fault injection: where justified by risk, restore the specific defect in an\n isolated copy, confirm failure, restore the fix, and confirm success again.\n\nFault injection is optional unless the assignment requires it. Never mutate another\nwriter's checkout, discard unrelated work, or leave deliberately broken code in the\ndeliverable. State an unavailable pre-fix run or missing test seam as a limitation.\n\nKeep the actual commands, results, and relevant environment conditions. Screenshots,\nlogs, and manually observed behavior can supplement tests; do not relabel them as\nautomated proof. A passing check and an independent acceptance decision are separate.\n", "providers/antigravity.json": '{\n "kind": "antigravity",\n "label": "Antigravity",\n "command": "agy",\n "enabled": true\n}\n', "providers/opencode.json": '{\n "kind": "acp",\n "label": "OpenCode",\n "command": "opencode",\n "args": ["acp"],\n "enabled": true\n}\n', "agents/supervisor/AGENT.md": "# Supervisor \u2014 process reviewer for main\n\nMain starts you beside itself in every Ph\u1EDF and Cafe session. You watch how main\nand its team work, not what they build: after each of main's turns ALP sends you\na digest of that turn \u2014 the user's request, main's tool calls and commands,\nassignments with their model, effort, mode and handoff, mail, questions to the\nuser, board pins, and main's final message. You never talk to the user, never\nchange files or git state, and never delegate.\n\n## What to check\n\nJudge the turn against ALP.md, main's AGENT.md, the selected team, and the\nlessons main has recorded. Judge main by its own session, which the digest\nstates: its model, runtime and instructions. Your system prompt describes your\nsession, not main's; never hold main to it (for example its model's name in\ncommit attribution). Look for process mistakes such as:\n\n- Ph\u1EDF: main created lead, or delegated work that needed no delegation while a\n difficult question went without oracle. Cafe: main worked around lead, sent a\n second stream of instructions to lead's peers, or intervened without\n transferring writer ownership.\n- An assignment without its objective, scope, constraints, verification, or\n expected handoff; an assignment whose model or effort clearly did not fit it.\n- Two writers in one checkout, edits without a board claim, or editing paths\n another agent claimed.\n- A logic or risky change accepted without reviewer, without real verification,\n or on a worker's word alone; main accepting its own change as reviewed.\n- Ending a turn while assignments ran, or claiming work, delegation, review, or\n checks that the digest does not show.\n- Asking the user what the code could answer, or not asking a decision that was\n the user's; writing for the user in a language other than the one ALP names.\n- Unclear requests: building or delegating a request that left open what to\n build, how far to go or how to judge it done, without first asking the user\n once, with options and a recommended default.\n- Leaving the user waiting. Each digest line has its local time, and ALP notes\n when main answered the user after their message, or that a message got no\n reply. A user who wrote and waited minutes for any answer is a mistake, as is\n long work started without telling the user what, who and when.\n- Blocking instead of working in the background: waiting on an assignment\n (`wait: true`, or `alp_wait` with nothing else to do) when the next step did\n not need its result, or running long commands in the foreground.\n- Tasks: closing a task with a logic change without reviewer or real\n verification; ignoring work a handoff listed as discovered without recording\n it as a task or saying why; leaving a task in review across turns; delegating\n work that belongs to an existing task without its taskId; removing a blocker\n only to start a blocked task; creating tasks for work finished in the same turn.\n- Repeating a mistake that a recorded lesson already covers.\n\nDo not review code quality, style, or the product decision itself: reviewer and\nthe user own those. A digest is a summary; when it is not enough, read the files,\nalp_board or alp_task before concluding. Do not report a mistake you cannot point to.\n\n## One note, or nothing\n\nWhen the turn was sound, send nothing. Otherwise send main one note with\n`alp_send` to `parent`, kind `note`, covering at most three of the most important\nmistakes. For each: what happened (cite the digest line), which rule it broke,\nand a question asking main why, and what rule it will follow from now on. When a\nrecorded lesson already covers it, say that it recurred. Main answers in its next\nturn and records the lesson; you do not need a reply.\n\nRead every lesson in the lessons files named in your instructions when you\nreview. A later lesson on the same point refines or replaces an earlier one:\na recurrence means main broke the latest applicable lesson, not an earlier\nversion of it. When three or\nmore lessons cover one theme, or a recorded lesson recurred, also suggest that\nmain distill them into a skill with alp_skill, and for which roles. When a\nmistake comes from ALP itself (an unclear instruction, a missing tool, a runtime\nbug), suggest that main propose an ALP issue with alp_issue. The user approves\nboth; you only suggest.\n\nEnd every review with a one-line verdict: `sound`, or the mistakes you asked about.\n", "agents/reviewer/AGENT.md": "# Reviewer \u2014 independent review of one diff\n\nYou are a senior engineer the technical coordinator calls to review one diff. Your first assignment\nis the complete brief: how to obtain the diff, the intended behavior, and the\nrules the change must follow. Review once. State assumptions for gaps; do not\ndepend on follow-up answers or invent missing evidence.\n\n## Scope and boundaries\n\n- Read project instructions and obtain the exact diff named in the brief.\n If the brief only describes the change, use\n `git diff --merge-base <upstream>`, with `origin/HEAD` as the default upstream,\n and list untracked files with `git ls-files --others --exclude-standard`.\n Read relevant untracked text files separately: they are absent from git diff.\n If the upstream or merge base cannot be resolved, report that limitation;\n do not silently choose a different baseline or change git state to create one.\n- Do not edit files, change git state, or run commands that mutate project data.\n Do not spawn agents, delegate work, or expand the assignment.\n- Read each changed file once where practical. Read other files only when they\n explain a change; revisit a file only to resolve a specific uncertainty.\n- If the diff is too large to review well, report that as the single finding,\n explain the scope limit, recommend a smaller review unit, and stop. Do not\n present a partial review as complete.\n\n## Review method\n\nStart with a short summary of the whole change. Then go file by file and hunk\nby hunk: describe what changed with the new-version line range, its relationship\nto other changes, and any bug, hack, unnecessary code, or shared mutable state.\nFor deleted-only code, explicitly label the old-version range rather than\ninventing new-version line numbers. Keep clean-hunk explanations concise and\nseparate them from actionable findings.\n\nJudge abstractions both ways: a layer that adds nothing may be inlined;\nduplication and branching may benefit from extraction. Name the exact places\nand recommend one concrete action only when it improves the code as it is now.\nDo not demand speculative architecture or unrelated cleanup.\n\nFor each finding give:\n\n- File and lines using the new version's numbers where they exist.\n- Severity: critical (security, data loss, crash), high (bug or real performance\n problem), medium (maintainability or minor bug), or low (style).\n- What is wrong, why it matters, and the recommended fix.\n- Evidence: distinguish behavior actually observed or checks personally run\n from inference and results reported by the brief. List checks not run without\n implying they passed.\n\nIf there are no actionable findings, say so and note verification limits.\n\n## Verdict\n\nEvery review ends in the same verdict, so the requester can act on it without\nreading prose:\n\n1. **Criteria.** List what the change must do and respect: the brief's\n acceptance criteria, or, when the brief has none, the ones you derived from\n it (say so). Judge each one `pass`, `fail` or `not_checked`, with its\n evidence: what you observed or ran, with file and lines. A `not_checked`\n says why.\n2. **Findings.** Each with severity, where (file:lines), the problem, and the fix.\n3. **Result.** It follows from the two:\n - `fail`: a criterion failed, or a finding is critical or high.\n - `pass_with_findings`: only medium or low findings.\n - `pass`: nothing to fix.\n - `blocked`: the review could not be done, for example because the diff is\n too large or the baseline cannot be resolved. The summary says what is\n needed.\n\n## One response\n\nReturn the review only to the requesting coordinator (main or lead), once, tied to the original brief.\nIn ALP, call `alp_handoff` with outcome `complete`, the review as the summary,\nand the verdict as `verdict` ({ result, criteria, findings }); ALP refuses a\nverdict whose result does not follow from its criteria and findings. Without\nALP, end the review with the verdict as a list in the same order:\n`VERDICT: FAIL`, then each criterion as `[FAIL] criterion: evidence`, then the\nfindings. Do not invent a `{tool:send}` tool, recipient, identifier, or\nsuccessful delivery. Do not contact the user or other roles. End your turn after\nthe response; the requester owns acceptance, fixes, and session cleanup.\n", "agents/peer/AGENT.md": "# Peer \u2014 independent bounded contributor\n\nReceive one bounded assignment from the technical coordinator (main in Ph\u1EDF,\nlead in Cafe). References to lead below mean that requesting coordinator. Apply your own judgment to the mechanism\nand evidence; your role is not to reproduce an assumed answer. Implementation,\nresearch, design, and review are dispositions supplied in the assignment.\n\n## Boundaries\n\n- Read ALP.md, this definition, and the assigned brief. Confirm root, objective,\n owned/excluded paths, authority, verification, and resource ownership before work.\n- Write only within the assigned scope and while holding its writer ownership.\n A read-only assignment does not authorize even a small implementation change.\n- Preserve unrelated user/agent changes. Do not stage or clean unrelated files.\n- Do not create subordinate agents, change topology, reassign work, or expand scope.\n- Report missing prerequisites or conflicting evidence to lead. Explain what is\n blocked and continue independent work still authorized by the brief.\n- You may question a premise with evidence and suggest a better approach. A design\n choice is not a user constraint unless its source establishes that requirement.\n- Answer exceptional direct questions from main or the user, while informing lead.\n Changes to work must be reconciled with lead's plan and writer ownership; escalate\n contradictory instructions rather than silently following two command chains.\n\n## Verification and handoff\n\nUse verification that tests the requested behavior. Do not weaken assertions or\ninvent outputs to claim success. Report checks not run and their practical limits.\nSend progress and material blockers to lead through available runtime channels.\n\nReturn:\n\n- Outcome: complete, partial, blocked, or premise needs reconsideration.\n- Candidate: actual artifact/files; base and candidate SHA when applicable.\n- Scope: paths changed/read and the assignment version used.\n- Verification: commands, actual results, and omitted checks.\n- Risks: unresolved findings, assumptions, and decisions needed.\n- Discovered: work you found outside your scope that should be tracked; your\n requester records it as a task.\n- Ownership: released or retained, with resources stopped or handed over.\n\nWhen your assignment carries a task, your handoff moves it to review; it closes\nonly when your requester accepts it. You may read the project's tasks with\nalp_task; only main and the user create or change them. Lead decides acceptance. Your handoff and passing tests do not self-accept the work.\nIf communication tools are unavailable, include the handoff in your result and\nstate the limitation; never claim a message was delivered without evidence.\n\n## Skills\n\nRead the relevant SKILL.md from the runtime skill index before using its method;\nfollow a required installed skill in the brief and report if it is missing.\nUse xia for research, bug-loop for diagnosis/regression,\nand smart-commits only when your authorized handoff includes local commits.\nRead-only work stops before project edits or commits. Missing user intent, planning,\nor delegation decisions return to lead; these skills do not authorize interviewing\nthe user, rewriting your brief, or spawning another agent.\n", "agents/oracle/AGENT.md": "# Oracle \u2014 bounded technical advisor\n\nYou are the advisor the technical coordinator calls for stronger reasoning: a plan, a review of its\nwork, an explanation of existing code, or a bug it cannot find. Your first\nassignment is the complete brief. Answer once; do not depend on follow-up answers.\nWhere information is missing, state your assumptions and proceed. If a missing\nfact prevents a reliable conclusion, identify the limit instead of inventing it.\nThe requester may ask two oracles on different models the same question to\ncompare their opinions; give your own independent view, not a guess at theirs.\n\n## Boundaries and judgment\n\n- Read project instructions, the brief, and its named files and facts first.\n Look further only when it makes the answer more accurate.\n- Do not edit files, change git state, or run commands that mutate project data.\n Do not spawn agents, delegate work, or expand the assignment.\n- Prefer the simplest solution that meets the stated needs: small changes that\n reuse existing code, patterns, and dependencies. Recommend a new service,\n library, or layer only when a concrete requirement justifies it. Avoid\n speculative scaling and future-proofing.\n- Give one recommendation. Add at most one alternative, only when its trade-off\n is materially different.\n- Match depth to the question: brief for small questions; deep only when needed.\n- In code reviews, report only the most important actionable issues. Cite the\n relevant files and lines; separate observed facts from inference.\n\n## One response\n\nUse only the sections that apply:\n\n1. TL;DR: the recommended approach in one to three sentences.\n2. Steps: a short numbered list; include a small code snippet only when helpful.\n3. Effort: S (under an hour), M (1 to 3 hours), L (1 to 2 days), or XL (more).\n4. Why: explain the recommendation and why alternatives are not needed now.\n5. Risks: relevant failure modes and how to guard against them.\n6. When to revisit: concrete signals that justify a more complex approach.\n\nReturn the response only to the requesting coordinator (main or lead), once, as advice tied to the\noriginal brief. If the runtime provides messaging with kind and reply metadata,\nuse kind `advice` and `reply_to` the brief's actual identifier. Otherwise return\nthe advice as the delegated task's final result. Do not invent a `{tool:send}`\ntool, recipient, identifier, or successful delivery. Do not contact the user or\nother roles. End your turn after the response; the requester owns any further action and\nsession cleanup.\n", "agents/main/AGENT.md": "# Main \u2014 active supervisor and user-facing owner\n\nYou are the user's primary point of contact and own delivery of the whole outcome.\nReceive requests, clarify only material unknowns, keep the user informed, and give\nthe final answer. The user should not have to coordinate lead and peer sessions.\nCommunicate with the user in the language ALP names for them (their setting; Vietnamese by default), even when delegated handoffs use another language.\n\n## Unclear requests\n\nWhen a request leaves open what to build, how far to go, or how to judge it done,\nin ways that change the work, ask once before you plan or delegate:\n\n- Ask two to four short questions together. Give each concrete options and your\n recommended default, and offer to decide with those defaults.\n- Use alp_ask with options while work runs; otherwise ask in your final message.\n- Do not ask what you can find out yourself, and do not question clear requests.\n- When the user lets you decide, state your choices in one line and go on.\n\n## Staying reachable\n\nWork like a chat where the work runs in the background: the user can talk with\nyou at any time.\n\n- Before work that takes more than a few minutes, tell the user in one line what\n you start, who does it, and when to expect it.\n- Delegate in the background, as alp_delegate does by default, and pass\n `etaMinutes`. Then end your turn rather than wait; ALP wakes you with results,\n questions and check-ins. Notes from assignments ride along; they do not wake you.\n- Pass `wait: true`, or alp_wait, only when your next step cannot go on without\n the result. When the user writes while you wait, ALP ends the wait early:\n answer them first in a short reply, steer the assignment their words change,\n then go on.\n- Run long shell commands (builds, test suites, servers, deploys) in the\n background when your tools allow it, and check their output later.\n- While assignments run, ALP sends you a check-in about every ten minutes, and\n when one passes its ETA. Tell the user in one or two lines how the work is\n going, and act on work that is late or silent.\n\n## Workflow and ownership\n\nRead the runtime's selected team. It stays fixed throughout this session.\n\n- Ph\u1EDF (default): you own technical execution as well as user communication.\n Implement directly or delegate bounded work directly to peer when useful.\n Do not create a lead session. A difficult focused task may need oracle rather\n than more workers; delegation is optional.\n- Cafe: assign technical execution to lead and supervise its outcome.\n Lead may implement directly or delegate to peer. Resolve scope, priorities,\n and missing user decisions. Route corrections through lead; do not issue a\n second stream of instructions to its peers. Transfer writer ownership before\n intervening in implementation.\n- Do not switch team mid-session. Recommend a new session if the user needs\n a different team; do not create a hidden extra coordination layer.\n- By default you run with full access to this machine. Use it for the user's\n task only; ask before anything destructive or outside the project that the\n user did not request.\n\n## Execution and advice\n\n- Preserve user requirements and existing changes. Inspect evidence before\n deciding. Keep the user informed of material findings and blockers.\n- In Ph\u1EDF, choose each peer's model and effort by the task's difficulty,\n ambiguity, risk, and autonomy needs: from peer's model profiles when they offer\n a choice (pick the model and its effort, and say why in modelReason), otherwise\n from the runtime catalog. Default to\n at most two simultaneous peers; increase only when the user requests it and\n the configured limit permits it. Never bypass limits with native spawn tools.\n- Use oracle for material uncertainty, difficult diagnosis, or architectural\n advice. Oracle runs on the configured oracle models listed in your runtime instructions. For a decision that deserves two independent opinions,\n start one oracle on each model in parallel and weigh where they agree and\n differ. If a model is unavailable, report it; never substitute another.\n- Use reviewer for logic changes and risky changes. Typo/format-only edits may\n skip review. In Cafe, lead arranges routine review; you may request an\n independent review of its final candidate when needed. Send fixes through lead.\n Brief reviewer with the acceptance criteria. Its handoff carries a verdict:\n each criterion with pass, fail or not_checked and evidence, and a result.\n Do not accept work on `fail` or `blocked`; weigh `pass_with_findings`.\n- Advisors are read-only and return once to their requester. Their advice is\n evidence for your judgment, not permission to expand the user's scope.\n- Consider runtime-provided plan, usage remaining, and reset timestamps when\n scheduling. Missing data means unknown, not unlimited; stale snapshots cannot\n guarantee availability. Do not infer subscription quota from token counts.\n- Each assignment includes root, objective, scope, constraints, ownership,\n verification, and expected handoff. Serialize writers in a shared checkout.\n Do not mutate files while waiting for a delegated writer.\n- Verify actual changes and reported checks before acceptance. Clearly identify\n your own changes and whether independent review ran. If delegation is not\n available, perform authorized work directly and report the limitation.\n\n## Tasks\n\nThe project's tasks live in `.alp/tasks`, one JSON file per task, committed with\nthe project. The user adds and edits them with `alp task`; you change them only\nwith alp_task, never by editing the files. Nobody else creates tasks: lead and\npeers report work they find to their requester, and you record it.\n\n- Create a task for work that outlives this turn, that the user asks you to\n track, or that you find outside the current scope (discoveredFrom: the task you\n were on). Do not create tasks for work you finish in this turn.\n- Model order with blockedBy and group larger work under an epic parent. Read\n alp_task ready before choosing what to do next; it lists open tasks that\n nothing blocks, most urgent first.\n- ALP lists the tasks in review, in progress and ready at the start of each of\n your turns. Start a task you take up yourself; give one to lead or peer by\n passing taskId to alp_delegate. That starts it, claims its paths for a writing\n assignment, and the handoff moves it to review.\n- Accept a task in review by closing it with a reason and a summary of the\n outcome and its evidence, only after verifying it with the same review rules\n as any other change; delegate it again with the same taskId for rework. Use\n wontfix, duplicate or superseded for work you drop.\n- Record the discovered work a handoff lists as tasks with discoveredFrom, or\n say why not.\n- When a task must wait for the user's decision, a time, a pull request or a CI\n run, add a gate with alp_task gate instead of remembering it. Only the user\n clears a human gate: ask them, and they approve it in Paseo or with the CLI.\n- For a workflow the project repeats, check alp_task formulas. Pour the\n matching formula with its vars instead of creating the steps one by one: it\n makes an epic with a task per step, ordered by blockedBy. A step marked as the\n user's waits on a human gate, and their approval completes it.\n\n## Supervisor and lessons\n\nA supervisor watches your process and may send you a note asking about a\nmistake. Answer it honestly in your reply, then record what you learned with\nalp_lesson: scope project for this project, user for every project. Recorded\nlessons return in your instructions in later sessions; follow them.\n\nWhen three or more lessons cover one theme, or a lesson recurs, distill them\ninto a skill with alp_skill: when to use it, the method as steps, and the checks.\nScope it to the roles whose work it guides: yourself, lead, peer, oracle,\nreviewer, supervisor, or a custom agent. List the lessons it replaces. The user\nsees the whole skill and approves it before it is saved.\n\n## Issues\n\nWhen you find a problem outside the task that is worth tracking, in this project\nor in ALP itself (its process, tools, agent instructions, or runtime), search\nwith alp_issue first, then propose a comment on a matching issue or a new one.\nWrite facts, reproduction, and evidence; never include secrets. The user\napproves every post. Never post issues or comments any other way.\n\n## Skills\n\nUse the runtime skill index to read the relevant SKILL.md before applying its method;\nload supporting references only when needed. Select directly from your scoped skills.\nFor material gaps in user intent use goal-griller; for unfamiliar implementation use\nxia; for dependent work use sequence-execution-plan. Before assigning work, use\nprompt-leverage to prepare a proportional, self-contained brief. When implementing\na correction use bug-loop; when local commits are requested use smart-commits.\nClear requests need no repeated interview, and skills grant no additional authority.\n", "agents/lead/AGENT.md": "# Lead \u2014 technical execution owner\n\nReceive the task from main and own technical delivery within its assigned root and\nscope. Main represents the user's goal and coordinates the broader outcome. Route\nquestions, progress, and the final deliverable to main; contact the user directly\nonly for an explicit exception.\n\n## Workflow and model choices\n\nYou own technical execution in Cafe. Small tasks may be completed directly;\npeer delegation is optional. Main remains the user-facing supervisor. Ph\u1EDF uses\nmain as technical coordinator and does not create this role.\n\nChoose model and effort separately for each peer assignment by the task's difficulty,\nuncertainty, risk, and autonomy needs. When peer's model profiles offer a choice (several\nmodels, or any model of a provider), pick a model they offer and its effort for every\nassignment and say why in modelReason; otherwise use the runtime catalog. The default\nlimit is two concurrent peers. Increase only at the user's request via configuration.\nSerialize writing assignments in the shared checkout; concurrent peers must be\nread-only. Never bypass these limits using native spawning or shell-launched agents.\n\nCall oracle for significant uncertainty and reviewer for logic changes or risky\nchanges; trivial typo/format edits may skip review. Brief reviewer with the\nacceptance criteria; its verdict judges each one with evidence, and a `fail` or\n`blocked` result goes back to work before you hand off. Oracle runs on the configured oracle models listed in your runtime instructions; for two independent\nopinions, consult both in parallel. Never substitute another model when one is\nunavailable; report it. Advisors return once to you; evaluate their evidence\nand report unresolved findings to main.\nUse available plan/usage/reset snapshots to avoid exhausted limits; missing or stale\ndata must remain explicitly uncertain and does not imply unlimited usage.\n\n## Background first\n\n- Delegate in the background, as alp_delegate does by default, with `etaMinutes`,\n and go on with work that does not depend on the result. Pass `wait: true` only\n when your next step cannot go on without it.\n- When only results are left, alp_wait for them. Mail from main ends the wait\n early: act on it, pass on what changes a peer's work with alp_send, then wait\n again.\n- Run long shell commands (builds, test suites, servers) in the background when\n your tools allow it.\n\n## Execution\n\n- Inspect the current implementation and user changes before planning work.\n- Decompose the outcome, resolve dependencies, assign ownership, and keep the plan\n current. Make technical decisions within the brief and project constraints.\n- Delegate bounded outcomes to peer using real runtime capabilities. A peer may\n implement, research, design, or review according to its brief; these are tasks,\n not additional built-in roles.\n- Give each peer the task identity, root, objective, constraint sources, owned and\n excluded paths, write/read authority, base revision when available, verification,\n resources, and expected handoff. Avoid prescribing an unverified solution.\n- Keep one writer per shared checkout. For concurrent writers allocate isolated\n workspaces and nonconflicting resources. Main intervention requires an explicit\n ownership transfer and an updated brief before work resumes.\n- You may implement directly. Identify your authored changes in the handoff so\n main can review them; do not self-accept your own implementation.\n- Check progress and blockers through supported runtime mechanisms. Missing\n messaging or delegation is a capability limitation, not proof of worker activity.\n\n## Review and escalation\n\nReview peer candidates using the actual diff/artifact and command outputs. Where\nGit is available, verify base and candidate revisions and owned paths. Issue an\nexplicit ACCEPT or REJECT with concrete findings; test success alone is insufficient.\nReturn your aggregate outcome, changed paths, verification, peer verdicts, risks,\nand ownership status to main. List work you or your peers found outside the scope\nunder discovered: only main and the user create tasks, and main records it. When\nmain gave you a task, your peers' claims carry its id, and your handoff moves it\nto review for main to accept. In a non-Git project, identify the actual changed\nfiles and evidence without inventing commit identifiers.\n\nPeer objections are evidence to investigate. Resolve technical matters in scope;\nsend scope/priority conflicts or missing authority to main. When main or the user\nchanges direction, update the plan, then deliver revised briefs to affected peers\nand confirm their receipt before relying on the new direction.\n\nIf delegation is unavailable, report that fact to main and perform authorized work\ndirectly where possible. Do not claim that a peer executed or reviewed the task.\n\n## Skills\n\nRead the applicable SKILL.md from the runtime skill index before using its method;\ndo not preload all bodies. Use xia for unfamiliar implementation,\nsequence-execution-plan for dependencies, and prompt-leverage before assigning peer.\nUse bug-loop for diagnosis/corrections and smart-commits when commits are part of\nyour authorized deliverable. Identify a relevant installed method in the peer brief.\nIntake with the user belongs to main: return material missing decisions to main,\nincluding them in the handoff when there is no live upstream messaging channel.\n" };
|
|
7814
7814
|
|
|
7815
7815
|
// packages/client/src/index.js
|
|
7816
|
-
import { createHash } from "node:crypto";
|
|
7817
|
-
import { open, readFile, realpath, stat, lstat, mkdir, readlink, unlink } from "node:fs/promises";
|
|
7816
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
7817
|
+
import { open, readFile, writeFile, realpath, stat, lstat, mkdir, readlink, unlink, readdir, rename, rmdir, link } from "node:fs/promises";
|
|
7818
7818
|
import os from "node:os";
|
|
7819
7819
|
import path from "node:path";
|
|
7820
7820
|
|
|
@@ -7945,7 +7945,7 @@ var AlpRpcError = class extends Error {
|
|
|
7945
7945
|
};
|
|
7946
7946
|
var ERROR = Object.freeze({ failed: 1e3, notFound: 1001, forbidden: 1003, conflict: 1004, unavailable: 1005, protocol: 1006 });
|
|
7947
7947
|
var RPC_ERROR = Object.freeze({ parseError: -32700, invalidRequest: -32600, methodNotFound: -32601, invalidParams: -32602 });
|
|
7948
|
-
var CAPABILITY = Object.freeze({ attachAfter: "attach.after", promptReceipts: "prompt.receipts", extensionMethods: "extension.methods", team: "team", task: "task", library: "library", settingsLanguage: "settings.language", sessionListPage: "session.list.page", questionWatch: "question.watch", questionWatchTurns: "question.watch.turns", workspaceFiles: "workspace.files", workspaceFilesRead: "workspace.files.read", promptImages: "prompt.images" });
|
|
7948
|
+
var CAPABILITY = Object.freeze({ attachAfter: "attach.after", promptReceipts: "prompt.receipts", extensionMethods: "extension.methods", team: "team", task: "task", library: "library", libraryProjectWrite: "library.project.write", settingsLanguage: "settings.language", sessionListPage: "session.list.page", questionWatch: "question.watch", questionWatchTurns: "question.watch.turns", workspaceFiles: "workspace.files", workspaceFilesRead: "workspace.files.read", promptImages: "prompt.images" });
|
|
7949
7949
|
var PROMPT_IMAGES = Object.freeze({ mimeTypes: Object.freeze(["image/png", "image/jpeg", "image/gif", "image/webp"]), maxCount: 8, maxBytes: 5 * 1024 * 1024, maxTotalBytes: 11 * 1024 * 1024 });
|
|
7950
7950
|
var base64Bytes = (data) => Math.floor(data.length * 3 / 4) - (data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0);
|
|
7951
7951
|
var RPC_METHODS = Object.freeze(["assignment.recall", "board.list", "catalog.get", "daemon.hello", "daemon.pause", "daemon.pauses", "daemon.resume", "daemon.shutdown", "daemon.status", "daemon.usage", "extension.register", "extension.unregister", "library.delete", "library.duplicate", "library.get", "library.list", "library.models", "library.rename", "library.save", "library.skills.set", "question.answer", "question.list", "question.watch", "session.archive", "session.attach", "session.close", "session.configure", "session.create", "session.get", "session.interrupt", "session.list", "session.log", "session.message", "session.preview", "session.prompt", "session.release", "session.rename", "session.status", "settings.language.get", "settings.language.set", "task.create", "task.gate.resolve", "task.list", "task.update", "team.list", "workspace.files.list", "workspace.files.read", "workspace.files.search"]);
|
|
@@ -8097,6 +8097,81 @@ async function acquireDaemonLock(home2, contents) {
|
|
|
8097
8097
|
}
|
|
8098
8098
|
throw new Error("Could not acquire the ALP daemon lock");
|
|
8099
8099
|
}
|
|
8100
|
+
async function lifecycleOwners(home2) {
|
|
8101
|
+
const directory = path.join(home2, "state", "alpd.starting");
|
|
8102
|
+
const names = await readdir(directory).catch((error51) => {
|
|
8103
|
+
if (error51.code === "ENOENT") return [];
|
|
8104
|
+
throw error51;
|
|
8105
|
+
});
|
|
8106
|
+
return names.map((token) => {
|
|
8107
|
+
const match = /^owner-(\d+)-(\d+)-(start|stop)-[a-f0-9-]+$/.exec(token);
|
|
8108
|
+
if (!match) throw new Error("Invalid alpd startup admission owner");
|
|
8109
|
+
return { token, pid: Number(match[1]), bootTime: Number(match[2]), action: match[3] };
|
|
8110
|
+
});
|
|
8111
|
+
}
|
|
8112
|
+
async function readDaemonLifecycle(home2) {
|
|
8113
|
+
return (await lifecycleOwners(home2)).find((owner) => lockAlive(owner));
|
|
8114
|
+
}
|
|
8115
|
+
async function withDaemonLifecycle(home2, action, run3, { joinStart = false } = {}) {
|
|
8116
|
+
try {
|
|
8117
|
+
await assertPrivateDirectory(home2, { create: true });
|
|
8118
|
+
} catch (cause) {
|
|
8119
|
+
throw Object.assign(new Error(`ALP_HOME_UNUSABLE, alpd failed to start while preparing its home: ${cause.message}`, { cause }), { code: "ALP_HOME_UNUSABLE" });
|
|
8120
|
+
}
|
|
8121
|
+
const state = path.join(home2, "state");
|
|
8122
|
+
await assertPrivateDirectory(state, { create: true });
|
|
8123
|
+
const directory = path.join(state, "alpd.starting");
|
|
8124
|
+
const token = `owner-${process.pid}-${bootTime()}-${action}-${randomUUID()}`;
|
|
8125
|
+
const candidate = path.join(state, `alpd.starting-${token}`);
|
|
8126
|
+
await mkdir(candidate, { mode: 448 });
|
|
8127
|
+
await writeFile(path.join(candidate, token), JSON.stringify({ pid: process.pid, bootTime: bootTime(), action }), { mode: 384 });
|
|
8128
|
+
try {
|
|
8129
|
+
let joined = false;
|
|
8130
|
+
if (joinStart) {
|
|
8131
|
+
const owner = await readDaemonLifecycle(home2);
|
|
8132
|
+
if (owner?.action === "start") {
|
|
8133
|
+
try {
|
|
8134
|
+
await link(path.join(directory, owner.token), path.join(directory, token));
|
|
8135
|
+
joined = true;
|
|
8136
|
+
} catch (error51) {
|
|
8137
|
+
if (error51.code !== "ENOENT") throw error51;
|
|
8138
|
+
}
|
|
8139
|
+
}
|
|
8140
|
+
}
|
|
8141
|
+
while (!joined) {
|
|
8142
|
+
try {
|
|
8143
|
+
await rename(candidate, directory);
|
|
8144
|
+
break;
|
|
8145
|
+
} catch (error51) {
|
|
8146
|
+
if (!["EEXIST", "ENOTEMPTY", "EPERM", "EACCES"].includes(error51.code)) throw error51;
|
|
8147
|
+
for (const owner of await lifecycleOwners(home2)) {
|
|
8148
|
+
if (!lockAlive(owner)) await unlink(path.join(directory, owner.token)).catch((error52) => {
|
|
8149
|
+
if (error52.code !== "ENOENT") throw error52;
|
|
8150
|
+
});
|
|
8151
|
+
}
|
|
8152
|
+
await rmdir(directory).catch((error52) => {
|
|
8153
|
+
if (!["ENOENT", "ENOTEMPTY", "EEXIST"].includes(error52.code)) throw error52;
|
|
8154
|
+
});
|
|
8155
|
+
await new Promise((resolve2) => setTimeout(resolve2, 25));
|
|
8156
|
+
}
|
|
8157
|
+
}
|
|
8158
|
+
try {
|
|
8159
|
+
return await run3();
|
|
8160
|
+
} finally {
|
|
8161
|
+
await unlink(path.join(directory, token));
|
|
8162
|
+
await rmdir(directory).catch((error51) => {
|
|
8163
|
+
if (!["ENOENT", "ENOTEMPTY", "EEXIST"].includes(error51.code)) throw error51;
|
|
8164
|
+
});
|
|
8165
|
+
}
|
|
8166
|
+
} finally {
|
|
8167
|
+
await unlink(path.join(candidate, token)).catch((error51) => {
|
|
8168
|
+
if (error51.code !== "ENOENT") throw error51;
|
|
8169
|
+
});
|
|
8170
|
+
await rmdir(candidate).catch((error51) => {
|
|
8171
|
+
if (error51.code !== "ENOENT") throw error51;
|
|
8172
|
+
});
|
|
8173
|
+
}
|
|
8174
|
+
}
|
|
8100
8175
|
|
|
8101
8176
|
// packages/runtime/src/runtime.ts
|
|
8102
8177
|
import { constants as fsConstants } from "node:fs";
|
|
@@ -8132,16 +8207,16 @@ var toolResult = (success2, value) => ({
|
|
|
8132
8207
|
var plainObject = (value, keys2) => !!value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).every((key2) => keys2.includes(key2));
|
|
8133
8208
|
|
|
8134
8209
|
// packages/runtime/src/board-domain.ts
|
|
8135
|
-
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
8136
|
-
import { appendFile, mkdir as mkdir2, readFile as readFile2, writeFile as
|
|
8210
|
+
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
8211
|
+
import { appendFile, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile3 } from "node:fs/promises";
|
|
8137
8212
|
|
|
8138
8213
|
// packages/core/src/atomic.js
|
|
8139
8214
|
import { randomBytes } from "node:crypto";
|
|
8140
|
-
import { rename, rm, writeFile } from "node:fs/promises";
|
|
8215
|
+
import { rename as rename2, rm, writeFile as writeFile2 } from "node:fs/promises";
|
|
8141
8216
|
async function moveFile(from, to) {
|
|
8142
8217
|
for (let attempt = 0; ; attempt++) {
|
|
8143
8218
|
try {
|
|
8144
|
-
return await
|
|
8219
|
+
return await rename2(from, to);
|
|
8145
8220
|
} catch (error51) {
|
|
8146
8221
|
if (process.platform !== "win32" || attempt >= 20 || !["EPERM", "EACCES", "EBUSY"].includes(error51?.code)) throw error51;
|
|
8147
8222
|
await new Promise((resolve2) => setTimeout(resolve2, 10 + attempt * 10));
|
|
@@ -8151,7 +8226,7 @@ async function moveFile(from, to) {
|
|
|
8151
8226
|
async function writeAtomic(file3, body, mode = 384) {
|
|
8152
8227
|
const temporary = `${file3}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
|
|
8153
8228
|
try {
|
|
8154
|
-
await
|
|
8229
|
+
await writeFile2(temporary, body, { mode });
|
|
8155
8230
|
await moveFile(temporary, file3);
|
|
8156
8231
|
} catch (error51) {
|
|
8157
8232
|
await rm(temporary, { force: true }).catch(() => {
|
|
@@ -8250,8 +8325,8 @@ function createBoardDomain({ options: options2, sessions, boards, loadedBoards,
|
|
|
8250
8325
|
const kept = prune([...pins.values()]);
|
|
8251
8326
|
boardWrites = boardWrites.then(async () => {
|
|
8252
8327
|
await mkdir2(options2.boardDir, { recursive: true });
|
|
8253
|
-
const temporary = `${boardFile(project)}.${
|
|
8254
|
-
await
|
|
8328
|
+
const temporary = `${boardFile(project)}.${randomUUID2().slice(0, 8)}.tmp`;
|
|
8329
|
+
await writeFile3(temporary, kept.map((pin) => JSON.stringify({ pin })).join("\n") + (kept.length ? "\n" : ""));
|
|
8255
8330
|
await moveFile(temporary, boardFile(project));
|
|
8256
8331
|
}).catch(() => {
|
|
8257
8332
|
});
|
|
@@ -8284,7 +8359,7 @@ function createBoardDomain({ options: options2, sessions, boards, loadedBoards,
|
|
|
8284
8359
|
function addPin(sessionId, session, pins, fields2) {
|
|
8285
8360
|
const project = session.mapping.agent.projectRoot;
|
|
8286
8361
|
const pin = {
|
|
8287
|
-
id: `p-${
|
|
8362
|
+
id: `p-${randomUUID2().slice(0, 8)}`,
|
|
8288
8363
|
project,
|
|
8289
8364
|
kind: fields2.kind,
|
|
8290
8365
|
body: fields2.body,
|
|
@@ -8518,8 +8593,8 @@ function describeVerification(verified) {
|
|
|
8518
8593
|
}
|
|
8519
8594
|
|
|
8520
8595
|
// packages/core/src/tasks.js
|
|
8521
|
-
import { mkdir as mkdir3, readFile as readFile4, readdir, rmdir, stat as stat2, writeFile as
|
|
8522
|
-
import { createHash as createHash3, randomUUID as
|
|
8596
|
+
import { mkdir as mkdir3, readFile as readFile4, readdir as readdir2, rmdir as rmdir2, stat as stat2, writeFile as writeFile4 } from "node:fs/promises";
|
|
8597
|
+
import { createHash as createHash3, randomUUID as randomUUID3 } from "node:crypto";
|
|
8523
8598
|
import path6 from "node:path";
|
|
8524
8599
|
|
|
8525
8600
|
// packages/core/src/safe-path.js
|
|
@@ -8576,7 +8651,7 @@ async function taskNames(projectRoot) {
|
|
|
8576
8651
|
await assertNoSymlinks(scope, "tasks");
|
|
8577
8652
|
let names;
|
|
8578
8653
|
try {
|
|
8579
|
-
names = await
|
|
8654
|
+
names = await readdir2(tasksDir(projectRoot));
|
|
8580
8655
|
} catch (error51) {
|
|
8581
8656
|
if (error51.code === "ENOENT") return [];
|
|
8582
8657
|
throw error51;
|
|
@@ -8772,7 +8847,7 @@ async function locked(projectRoot, work) {
|
|
|
8772
8847
|
if (error51.code !== "EEXIST") throw error51;
|
|
8773
8848
|
const age = await stat2(lock).then((info) => Date.now() - info.mtimeMs, () => 0);
|
|
8774
8849
|
if (age > LOCK_STALE_MS) {
|
|
8775
|
-
await
|
|
8850
|
+
await rmdir2(lock).catch(() => {
|
|
8776
8851
|
});
|
|
8777
8852
|
continue;
|
|
8778
8853
|
}
|
|
@@ -8783,7 +8858,7 @@ async function locked(projectRoot, work) {
|
|
|
8783
8858
|
try {
|
|
8784
8859
|
return await work();
|
|
8785
8860
|
} finally {
|
|
8786
|
-
await
|
|
8861
|
+
await rmdir2(lock).catch(() => {
|
|
8787
8862
|
});
|
|
8788
8863
|
}
|
|
8789
8864
|
});
|
|
@@ -8793,15 +8868,15 @@ async function locked(projectRoot, work) {
|
|
|
8793
8868
|
var withTaskLock = (projectRoot, work) => locked(projectRoot, work);
|
|
8794
8869
|
async function prepare(dir) {
|
|
8795
8870
|
await mkdir3(dir, { recursive: true });
|
|
8796
|
-
await
|
|
8871
|
+
await writeFile4(path6.join(dir, ".gitignore"), GITIGNORE, { flag: "wx" }).catch((error51) => {
|
|
8797
8872
|
if (error51.code !== "EEXIST") throw error51;
|
|
8798
8873
|
});
|
|
8799
8874
|
}
|
|
8800
8875
|
async function save(projectRoot, task) {
|
|
8801
8876
|
const file3 = taskFile(projectRoot, task.id);
|
|
8802
8877
|
await assertNoSymlinks(path6.join(path6.resolve(projectRoot), ".alp"), path6.join("tasks", `${task.id}.json`));
|
|
8803
|
-
const temporary = `${file3}.${
|
|
8804
|
-
await
|
|
8878
|
+
const temporary = `${file3}.${randomUUID3().slice(0, 8)}.tmp`;
|
|
8879
|
+
await writeFile4(temporary, JSON.stringify(task, null, 2) + "\n");
|
|
8805
8880
|
await moveFile(temporary, file3);
|
|
8806
8881
|
return task;
|
|
8807
8882
|
}
|
|
@@ -8812,7 +8887,7 @@ function newId(tasks, title, parent) {
|
|
|
8812
8887
|
const used = tasks.filter((task) => task.id.startsWith(prefix) && !task.id.slice(prefix.length).includes(".")).map((task) => Number(task.id.slice(prefix.length)));
|
|
8813
8888
|
return `${prefix}${Math.max(0, ...used) + 1}`;
|
|
8814
8889
|
}
|
|
8815
|
-
const hash2 = createHash3("sha256").update(`${title}\0${now()}\0${
|
|
8890
|
+
const hash2 = createHash3("sha256").update(`${title}\0${now()}\0${randomUUID3()}`).digest("hex");
|
|
8816
8891
|
for (let length = 4; length <= 12; length++) if (!taken.has(`t-${hash2.slice(0, length)}`)) return `t-${hash2.slice(0, length)}`;
|
|
8817
8892
|
return fail2("INVALID_TASK", "Could not choose a free task id");
|
|
8818
8893
|
}
|
|
@@ -9295,7 +9370,7 @@ function batch(projectRoot, work, { dryRun = false } = {}) {
|
|
|
9295
9370
|
// packages/runtime/src/workspace.ts
|
|
9296
9371
|
import { spawn as spawn2 } from "node:child_process";
|
|
9297
9372
|
import { createHash as createHash4 } from "node:crypto";
|
|
9298
|
-
import { copyFile, lstat as lstat3, mkdir as mkdir4, mkdtemp, readdir as
|
|
9373
|
+
import { copyFile, lstat as lstat3, mkdir as mkdir4, mkdtemp, readdir as readdir3, readFile as readFile5, realpath as realpath2, rm as rm2, stat as stat3, symlink, unlink as unlink2, writeFile as writeFile5 } from "node:fs/promises";
|
|
9299
9374
|
import os2 from "node:os";
|
|
9300
9375
|
import path7 from "node:path";
|
|
9301
9376
|
var IDENTITY = ["-c", "user.name=ALP", "-c", "user.email=alp@localhost", "-c", "commit.gpgsign=false"];
|
|
@@ -9364,7 +9439,7 @@ async function mergeText(ours, base2, theirs) {
|
|
|
9364
9439
|
const directory = await mkdtemp(path7.join(os2.tmpdir(), "alp-merge-"));
|
|
9365
9440
|
try {
|
|
9366
9441
|
const [o, b, t] = ["ours", "base", "theirs"].map((name2) => path7.join(directory, name2));
|
|
9367
|
-
await Promise.all([
|
|
9442
|
+
await Promise.all([writeFile5(o, ours), writeFile5(b, base2), writeFile5(t, theirs)]);
|
|
9368
9443
|
const result = await git(directory, ["merge-file", "-p", "-L", "yours", "-L", "base", "-L", "assignment", o, b, t]);
|
|
9369
9444
|
if (result.code < 0 || result.code > 127) throw new Error(`git merge-file failed: ${result.stderr.trim()}`);
|
|
9370
9445
|
return { content: result.stdout, conflict: result.code > 0 };
|
|
@@ -9447,7 +9522,7 @@ async function mergeWorktree(worktree, change) {
|
|
|
9447
9522
|
if (same(ours, base2)) {
|
|
9448
9523
|
if (theirs) {
|
|
9449
9524
|
await mkdir4(path7.dirname(target), { recursive: true });
|
|
9450
|
-
await
|
|
9525
|
+
await writeFile5(target, theirs);
|
|
9451
9526
|
} else {
|
|
9452
9527
|
await rm2(target, { force: true });
|
|
9453
9528
|
}
|
|
@@ -9458,7 +9533,7 @@ async function mergeWorktree(worktree, change) {
|
|
|
9458
9533
|
continue;
|
|
9459
9534
|
}
|
|
9460
9535
|
const merged = await mergeText(ours, base2 ?? Buffer.alloc(0), theirs);
|
|
9461
|
-
await
|
|
9536
|
+
await writeFile5(target, merged.content);
|
|
9462
9537
|
if (merged.conflict) conflicts.push(file3);
|
|
9463
9538
|
}
|
|
9464
9539
|
return conflicts.length ? { status: "conflicts", files: change.files, conflicts } : { status: "applied", files: change.files };
|
|
@@ -9471,7 +9546,7 @@ async function removeWorktree(worktree, { deleteBranch = false } = {}) {
|
|
|
9471
9546
|
}
|
|
9472
9547
|
async function reclaimWorktrees(root) {
|
|
9473
9548
|
const kept = [];
|
|
9474
|
-
const entries2 = await
|
|
9549
|
+
const entries2 = await readdir3(root).catch(() => []);
|
|
9475
9550
|
for (const name2 of entries2) {
|
|
9476
9551
|
const target = path7.join(root, name2);
|
|
9477
9552
|
if (!(await stat3(target).catch(() => void 0))?.isDirectory()) continue;
|
|
@@ -9525,7 +9600,7 @@ async function removeCopy(copy) {
|
|
|
9525
9600
|
}
|
|
9526
9601
|
async function reclaimCopies(root) {
|
|
9527
9602
|
let removed = 0;
|
|
9528
|
-
for (const name2 of await
|
|
9603
|
+
for (const name2 of await readdir3(root).catch(() => [])) {
|
|
9529
9604
|
const target = path7.join(root, name2);
|
|
9530
9605
|
const common = await git(target, ["rev-parse", "--path-format=absolute", "--git-common-dir"]).catch(() => void 0);
|
|
9531
9606
|
if (common?.code === 0) await removeCopy({ checkout: path7.dirname(common.stdout.trim()), path: target, workdir: target });
|
|
@@ -9650,7 +9725,7 @@ function claudeUsage(result) {
|
|
|
9650
9725
|
function usageAllows(usage) {
|
|
9651
9726
|
if (!usage?.available) return void 0;
|
|
9652
9727
|
const limits = Array.isArray(usage.limits) ? usage.limits : [];
|
|
9653
|
-
if (limits.some((limit) => limit?.spendControlReached === true)) return false;
|
|
9728
|
+
if (usage.ordinaryUsageAllowed === false || limits.some((limit) => limit?.spendControlReached === true)) return false;
|
|
9654
9729
|
const windows = limits.length ? limits.flatMap((limit) => limit?.windows ?? []) : usage.windows ?? [];
|
|
9655
9730
|
const known = windows.filter((window) => typeof window?.usedPercent === "number");
|
|
9656
9731
|
if (!known.length) return void 0;
|
|
@@ -9869,7 +9944,7 @@ function writablePath(file3, workdir) {
|
|
|
9869
9944
|
import { accessSync, constants, existsSync, mkdirSync, realpathSync as realpathSync3, rmdirSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
|
|
9870
9945
|
import os4 from "node:os";
|
|
9871
9946
|
import path10 from "node:path";
|
|
9872
|
-
import { randomUUID as
|
|
9947
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
9873
9948
|
|
|
9874
9949
|
// node_modules/zod/v4/classic/external.js
|
|
9875
9950
|
var external_exports = {};
|
|
@@ -24643,7 +24718,10 @@ function antigravityToolDetail(info) {
|
|
|
24643
24718
|
return { type: "unknown", input: tool?.parameters ?? null, output: tool?.error?.message ?? output ?? null };
|
|
24644
24719
|
}
|
|
24645
24720
|
function withHeaders(path41, diff) {
|
|
24646
|
-
|
|
24721
|
+
if (/^(--- |diff --git )/.test(diff)) return diff;
|
|
24722
|
+
if (diff.startsWith("+++ ")) return `--- a/${path41}
|
|
24723
|
+
${diff}`;
|
|
24724
|
+
return `--- a/${path41}
|
|
24647
24725
|
+++ b/${path41}
|
|
24648
24726
|
${diff}`;
|
|
24649
24727
|
}
|
|
@@ -24698,8 +24776,8 @@ function codexToolCall(item, done) {
|
|
|
24698
24776
|
|
|
24699
24777
|
// packages/core/src/permissions.js
|
|
24700
24778
|
import path9 from "node:path";
|
|
24701
|
-
import { randomUUID as
|
|
24702
|
-
import { readFile as readFile6, writeFile as
|
|
24779
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
24780
|
+
import { readFile as readFile6, writeFile as writeFile6 } from "node:fs/promises";
|
|
24703
24781
|
var BASES = ["read-only", "workspace-write", "full-access"];
|
|
24704
24782
|
var RANK = { "read-only": 0, "workspace-write": 1, "full-access": 2 };
|
|
24705
24783
|
var ADVISORS = ["oracle", "reviewer", "supervisor"];
|
|
@@ -24813,8 +24891,8 @@ async function addAllowRule(projectRoot, home2, profileName, rule) {
|
|
|
24813
24891
|
if (!object3(profile)) continue;
|
|
24814
24892
|
profile.allow = [.../* @__PURE__ */ new Set([...profile.allow ?? [], rule])];
|
|
24815
24893
|
validatePermissions(settings.permissions, file3);
|
|
24816
|
-
const temporary = `${file3}.${
|
|
24817
|
-
await
|
|
24894
|
+
const temporary = `${file3}.${randomUUID4().slice(0, 8)}.tmp`;
|
|
24895
|
+
await writeFile6(temporary, `${JSON.stringify(settings, null, 2)}
|
|
24818
24896
|
`);
|
|
24819
24897
|
await moveFile(temporary, file3);
|
|
24820
24898
|
return file3;
|
|
@@ -24952,12 +25030,14 @@ var toolShapes = {
|
|
|
24952
25030
|
body: external_exports.string().min(1).max(2e4),
|
|
24953
25031
|
roles: external_exports.array(external_exports.string().min(1)).min(1).max(10),
|
|
24954
25032
|
lessons: external_exports.array(external_exports.string().min(1).max(600)).max(50).optional(),
|
|
24955
|
-
replace: external_exports.boolean().optional()
|
|
25033
|
+
replace: external_exports.boolean().optional(),
|
|
25034
|
+
requestId: external_exports.string().min(1).max(100).optional()
|
|
24956
25035
|
},
|
|
24957
25036
|
alp_issue: {
|
|
24958
25037
|
action: external_exports.enum(["search", "create", "comment"]),
|
|
24959
25038
|
target: external_exports.enum(["project", "alp"]),
|
|
24960
25039
|
query: external_exports.string().min(1).max(200).optional(),
|
|
25040
|
+
requestId: external_exports.string().min(1).max(100).optional(),
|
|
24961
25041
|
title: external_exports.string().min(1).max(200).optional(),
|
|
24962
25042
|
body: external_exports.string().min(1).max(2e4).optional(),
|
|
24963
25043
|
issue: external_exports.number().int().positive().optional(),
|
|
@@ -25186,6 +25266,7 @@ var ClaudeTransport = class {
|
|
|
25186
25266
|
activeTurn;
|
|
25187
25267
|
closed = false;
|
|
25188
25268
|
assistantText = /* @__PURE__ */ new Map();
|
|
25269
|
+
streamingMessage;
|
|
25189
25270
|
toolCalls = /* @__PURE__ */ new Map();
|
|
25190
25271
|
/** The last rate limit Claude reported, and whether the running turn hit one. */
|
|
25191
25272
|
rateLimit;
|
|
@@ -25254,8 +25335,9 @@ var ClaudeTransport = class {
|
|
|
25254
25335
|
return {};
|
|
25255
25336
|
}
|
|
25256
25337
|
if (method === "thread/start" || method === "thread/resume" || method === "thread/fork") {
|
|
25338
|
+
this.clearAssistantText();
|
|
25257
25339
|
this.config = params;
|
|
25258
|
-
this.threadId = method === "thread/resume" ? params.threadId :
|
|
25340
|
+
this.threadId = method === "thread/resume" ? params.threadId : randomUUID5();
|
|
25259
25341
|
await this.start(method === "thread/start" ? void 0 : { resume: params.threadId, fork: method === "thread/fork" });
|
|
25260
25342
|
return {
|
|
25261
25343
|
thread: { id: this.threadId, turns: [] },
|
|
@@ -25266,8 +25348,11 @@ var ClaudeTransport = class {
|
|
|
25266
25348
|
}
|
|
25267
25349
|
if (method === "turn/start" || method === "turn/steer") {
|
|
25268
25350
|
if (!this.query) throw new Error("Claude thread is not initialized");
|
|
25269
|
-
const turnId = method === "turn/steer" ? params.expectedTurnId :
|
|
25270
|
-
if (method === "turn/start")
|
|
25351
|
+
const turnId = method === "turn/steer" ? params.expectedTurnId : randomUUID5();
|
|
25352
|
+
if (method === "turn/start") {
|
|
25353
|
+
this.clearAssistantText();
|
|
25354
|
+
this.activeTurn = turnId;
|
|
25355
|
+
}
|
|
25271
25356
|
this.input.push({
|
|
25272
25357
|
type: "user",
|
|
25273
25358
|
message: {
|
|
@@ -25275,12 +25360,13 @@ var ClaudeTransport = class {
|
|
|
25275
25360
|
content: params.input.map(claudeContent)
|
|
25276
25361
|
},
|
|
25277
25362
|
parent_tool_use_id: null,
|
|
25278
|
-
uuid:
|
|
25363
|
+
uuid: randomUUID5(),
|
|
25279
25364
|
session_id: this.threadId
|
|
25280
25365
|
});
|
|
25281
25366
|
return method === "turn/start" ? { turn: { id: turnId } } : {};
|
|
25282
25367
|
}
|
|
25283
25368
|
if (method === "turn/interrupt") {
|
|
25369
|
+
this.streamingMessage = void 0;
|
|
25284
25370
|
await this.query?.interrupt();
|
|
25285
25371
|
return {};
|
|
25286
25372
|
}
|
|
@@ -25304,7 +25390,7 @@ var ClaudeTransport = class {
|
|
|
25304
25390
|
isError: true
|
|
25305
25391
|
};
|
|
25306
25392
|
}
|
|
25307
|
-
const callId = `claude-tool-${
|
|
25393
|
+
const callId = `claude-tool-${randomUUID5()}`;
|
|
25308
25394
|
this.emit("item/started", {
|
|
25309
25395
|
threadId: this.threadId,
|
|
25310
25396
|
item: {
|
|
@@ -25364,7 +25450,7 @@ var ClaudeTransport = class {
|
|
|
25364
25450
|
const plugins = [];
|
|
25365
25451
|
if (config2.skills?.length) {
|
|
25366
25452
|
try {
|
|
25367
|
-
this.skillDirectory ??= path10.join(os4.tmpdir(), "alp-skills",
|
|
25453
|
+
this.skillDirectory ??= path10.join(os4.tmpdir(), "alp-skills", randomUUID5());
|
|
25368
25454
|
plugins.push({ type: "local", path: skillPlugin(this.skillDirectory, config2.skills), skipMcpDiscovery: true });
|
|
25369
25455
|
} catch {
|
|
25370
25456
|
}
|
|
@@ -25384,7 +25470,7 @@ var ClaudeTransport = class {
|
|
|
25384
25470
|
...plugins.length ? { plugins } : {},
|
|
25385
25471
|
persistSession: !config2.ephemeral,
|
|
25386
25472
|
promptSuggestions: false,
|
|
25387
|
-
includePartialMessages:
|
|
25473
|
+
includePartialMessages: true,
|
|
25388
25474
|
...from ? { resume: from.resume, ...from.fork ? { forkSession: true, sessionId: this.threadId } : {} } : { sessionId: this.threadId },
|
|
25389
25475
|
// Explicit native path avoids SDK optional binaries inside Electron app.asar.
|
|
25390
25476
|
pathToClaudeCodeExecutable: this.command
|
|
@@ -25422,30 +25508,57 @@ var ClaudeTransport = class {
|
|
|
25422
25508
|
if (!this.closed) this.fail(new Error("Claude session exited"));
|
|
25423
25509
|
}
|
|
25424
25510
|
handle(message) {
|
|
25511
|
+
if (this.closed) return;
|
|
25425
25512
|
if (message.type === "rate_limit_event") {
|
|
25426
25513
|
this.rateLimit = message.rate_limit_info;
|
|
25427
25514
|
this.emit("account/rateLimits/updated", { claude: message.rate_limit_info });
|
|
25428
25515
|
return;
|
|
25429
25516
|
}
|
|
25430
|
-
|
|
25431
|
-
|
|
25517
|
+
const streamStart = message.type === "stream_event" && message.event?.type === "message_start";
|
|
25518
|
+
if ((message.type === "assistant" || streamStart) && !message.parent_tool_use_id && !this.activeTurn) {
|
|
25519
|
+
this.activeTurn = randomUUID5();
|
|
25432
25520
|
this.emit("turn/started", { threadId: this.threadId, turn: { id: this.activeTurn } });
|
|
25433
25521
|
}
|
|
25522
|
+
if (message.type === "stream_event") {
|
|
25523
|
+
if (message.parent_tool_use_id) return;
|
|
25524
|
+
const event = message.event;
|
|
25525
|
+
if (event.type === "message_start") {
|
|
25526
|
+
this.streamingMessage = event.message.id;
|
|
25527
|
+
} else if (event.type === "message_stop") {
|
|
25528
|
+
this.streamingMessage = void 0;
|
|
25529
|
+
} else if (this.streamingMessage) {
|
|
25530
|
+
const id = this.streamingMessage;
|
|
25531
|
+
const state = this.assistantState(id);
|
|
25532
|
+
let delta;
|
|
25533
|
+
if (event.type === "content_block_start" && event.content_block.type === "text") {
|
|
25534
|
+
state.blocks.set(event.index, { text: event.content_block.text, final: false });
|
|
25535
|
+
delta = event.content_block.text;
|
|
25536
|
+
} else if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
|
|
25537
|
+
const block = state.blocks.get(event.index);
|
|
25538
|
+
if (block && !block.final) {
|
|
25539
|
+
block.text += event.delta.text;
|
|
25540
|
+
delta = event.delta.text;
|
|
25541
|
+
}
|
|
25542
|
+
}
|
|
25543
|
+
if (delta) this.emit("item/agentMessage/delta", { threadId: this.threadId, turnId: this.activeTurn, itemId: id, delta });
|
|
25544
|
+
}
|
|
25545
|
+
return;
|
|
25546
|
+
}
|
|
25434
25547
|
if (message.type === "assistant" && message.error === "rate_limit") this.limitedTurn = true;
|
|
25435
25548
|
if (message.type === "system" && (message.subtype === "status" || message.subtype === "compact_boundary")) {
|
|
25436
25549
|
const system = message;
|
|
25437
25550
|
if (system.subtype === "status" && system.status === "compacting" && !this.compaction) {
|
|
25438
|
-
this.compaction =
|
|
25551
|
+
this.compaction = randomUUID5();
|
|
25439
25552
|
this.emit("item/started", { threadId: this.threadId, turnId: this.activeTurn, item: { type: "contextCompaction", id: this.compaction } });
|
|
25440
25553
|
} else if (system.subtype === "status" && system.compact_result === "failed") {
|
|
25441
|
-
this.emit("item/completed", { threadId: this.threadId, turnId: this.activeTurn, item: { type: "contextCompaction", id: this.compaction ??
|
|
25554
|
+
this.emit("item/completed", { threadId: this.threadId, turnId: this.activeTurn, item: { type: "contextCompaction", id: this.compaction ?? randomUUID5(), status: "failed", error: system.compact_error } });
|
|
25442
25555
|
this.compaction = void 0;
|
|
25443
25556
|
} else if (system.subtype === "compact_boundary") {
|
|
25444
25557
|
const meta3 = system.compact_metadata ?? {};
|
|
25445
25558
|
this.emit("item/completed", {
|
|
25446
25559
|
threadId: this.threadId,
|
|
25447
25560
|
turnId: this.activeTurn,
|
|
25448
|
-
item: { type: "contextCompaction", id: this.compaction ??
|
|
25561
|
+
item: { type: "contextCompaction", id: this.compaction ?? randomUUID5(), status: "completed", trigger: meta3.trigger, preTokens: meta3.pre_tokens, postTokens: meta3.post_tokens }
|
|
25449
25562
|
});
|
|
25450
25563
|
this.compaction = void 0;
|
|
25451
25564
|
}
|
|
@@ -25468,14 +25581,18 @@ var ClaudeTransport = class {
|
|
|
25468
25581
|
if (windows.length && !this.compactAt) this.contextWindow = Math.max(...windows);
|
|
25469
25582
|
}
|
|
25470
25583
|
if (message.type === "assistant" && !message.parent_tool_use_id) {
|
|
25584
|
+
const id = message.message.id;
|
|
25585
|
+
const state = this.assistantState(id);
|
|
25586
|
+
if (message.uuid && state.seen.has(message.uuid)) return;
|
|
25587
|
+
if (message.uuid) state.seen.add(message.uuid);
|
|
25471
25588
|
for (const block of message.message.content) {
|
|
25472
25589
|
if (block.type === "text") {
|
|
25473
|
-
const
|
|
25474
|
-
const
|
|
25475
|
-
|
|
25590
|
+
const pending = [...state.blocks.entries()].sort(([a], [b]) => a - b).find(([, value]) => !value.final);
|
|
25591
|
+
const index = pending?.[0] ?? Math.max(-1, ...state.blocks.keys()) + 1;
|
|
25592
|
+
state.blocks.set(index, { text: block.text, final: true });
|
|
25476
25593
|
this.emit("item/completed", {
|
|
25477
25594
|
threadId: this.threadId,
|
|
25478
|
-
item: { type: "agentMessage", id, text:
|
|
25595
|
+
item: { type: "agentMessage", id, text: [...state.blocks.entries()].sort(([a], [b]) => a - b).map(([, block2]) => block2.text).join("") }
|
|
25479
25596
|
});
|
|
25480
25597
|
} else if (block.type === "tool_use") {
|
|
25481
25598
|
const command = block.name === "Bash" ? String(block.input?.command ?? "") : `${block.name} ${JSON.stringify(block.input ?? {})}`;
|
|
@@ -25524,6 +25641,7 @@ var ClaudeTransport = class {
|
|
|
25524
25641
|
}
|
|
25525
25642
|
return;
|
|
25526
25643
|
}
|
|
25644
|
+
if (message.type === "result") this.clearAssistantText();
|
|
25527
25645
|
if (message.type === "result" && this.activeTurn) {
|
|
25528
25646
|
const turnId = this.activeTurn;
|
|
25529
25647
|
this.activeTurn = void 0;
|
|
@@ -25546,14 +25664,25 @@ var ClaudeTransport = class {
|
|
|
25546
25664
|
emit(method, params) {
|
|
25547
25665
|
for (const listener of this.listeners) listener(method, params);
|
|
25548
25666
|
}
|
|
25667
|
+
assistantState(id) {
|
|
25668
|
+
let state = this.assistantText.get(id);
|
|
25669
|
+
if (!state) this.assistantText.set(id, state = { blocks: /* @__PURE__ */ new Map(), seen: /* @__PURE__ */ new Set() });
|
|
25670
|
+
return state;
|
|
25671
|
+
}
|
|
25672
|
+
clearAssistantText() {
|
|
25673
|
+
this.streamingMessage = void 0;
|
|
25674
|
+
this.assistantText.clear();
|
|
25675
|
+
}
|
|
25549
25676
|
fail(value) {
|
|
25550
25677
|
if (this.closed) return;
|
|
25678
|
+
this.clearAssistantText();
|
|
25551
25679
|
const error51 = value instanceof Error ? value : new Error(String(value));
|
|
25552
25680
|
for (const listener of this.failures) listener(error51);
|
|
25553
25681
|
}
|
|
25554
25682
|
async close() {
|
|
25555
25683
|
if (this.closed) return;
|
|
25556
25684
|
this.closed = true;
|
|
25685
|
+
this.clearAssistantText();
|
|
25557
25686
|
this.input.close();
|
|
25558
25687
|
this.query?.close();
|
|
25559
25688
|
await this.pump?.catch(() => {
|
|
@@ -25574,7 +25703,7 @@ var ClaudeTransport = class {
|
|
|
25574
25703
|
|
|
25575
25704
|
// packages/runtime/src/acp-transport.ts
|
|
25576
25705
|
import { spawn as spawn4, execFile as execFile3 } from "node:child_process";
|
|
25577
|
-
import { randomBytes as randomBytes2, randomUUID as
|
|
25706
|
+
import { randomBytes as randomBytes2, randomUUID as randomUUID6 } from "node:crypto";
|
|
25578
25707
|
import { rmSync as rmSync2 } from "node:fs";
|
|
25579
25708
|
import net from "node:net";
|
|
25580
25709
|
import os5 from "node:os";
|
|
@@ -25583,7 +25712,7 @@ import { createInterface as createInterface2 } from "node:readline";
|
|
|
25583
25712
|
|
|
25584
25713
|
// packages/core/src/providers.js
|
|
25585
25714
|
import { existsSync as existsSync2, readFileSync } from "node:fs";
|
|
25586
|
-
import { readdir as
|
|
25715
|
+
import { readdir as readdir4, readFile as readFile7 } from "node:fs/promises";
|
|
25587
25716
|
import path12 from "node:path";
|
|
25588
25717
|
|
|
25589
25718
|
// packages/core/src/validation.js
|
|
@@ -25911,7 +26040,7 @@ async function listProviders(library) {
|
|
|
25911
26040
|
if (!library) return [];
|
|
25912
26041
|
let names = [];
|
|
25913
26042
|
try {
|
|
25914
|
-
names = await
|
|
26043
|
+
names = await readdir4(path12.join(library, "providers"));
|
|
25915
26044
|
} catch (error51) {
|
|
25916
26045
|
if (error51.code !== "ENOENT") throw error51;
|
|
25917
26046
|
}
|
|
@@ -26132,7 +26261,7 @@ var AcpTransport = class {
|
|
|
26132
26261
|
if (!this.sessionId) throw new Error(`${this.provider.label} session is not open`);
|
|
26133
26262
|
if (this.prompting) await Promise.race([this.prompting, new Promise((resolve2) => setTimeout(resolve2, 1e4).unref())]);
|
|
26134
26263
|
if (this.closed) throw new Error("Runtime is closed");
|
|
26135
|
-
const turnId =
|
|
26264
|
+
const turnId = randomUUID6();
|
|
26136
26265
|
this.activeTurn = turnId;
|
|
26137
26266
|
this.message = void 0;
|
|
26138
26267
|
const prompt = (params.input ?? []).map(acpContent).filter(Boolean);
|
|
@@ -26374,7 +26503,7 @@ ${this.config.developerInstructions}` });
|
|
|
26374
26503
|
if (!this.requestHandler || !this.activeTurn || !this.tools.some((candidate) => candidate.name === tool)) {
|
|
26375
26504
|
return { content: [{ type: "text", text: this.activeTurn ? `Unknown ALP tool '${tool}'` : "ALP tools are unavailable between turns" }], isError: true };
|
|
26376
26505
|
}
|
|
26377
|
-
const callId = `acp-tool-${
|
|
26506
|
+
const callId = `acp-tool-${randomUUID6()}`;
|
|
26378
26507
|
const item = { type: "dynamicToolCall", id: callId, callId, tool, arguments: args };
|
|
26379
26508
|
this.closeMessage();
|
|
26380
26509
|
this.emit("item/started", { threadId: this.sessionId, item: { ...item, status: "inProgress" } });
|
|
@@ -26438,9 +26567,9 @@ ${this.config.developerInstructions}` });
|
|
|
26438
26567
|
|
|
26439
26568
|
// packages/runtime/src/antigravity-transport.ts
|
|
26440
26569
|
import { spawn as spawn6, execFile as execFile4 } from "node:child_process";
|
|
26441
|
-
import { randomBytes as randomBytes3, randomUUID as
|
|
26570
|
+
import { randomBytes as randomBytes3, randomUUID as randomUUID7 } from "node:crypto";
|
|
26442
26571
|
import { rmSync as rmSync3 } from "node:fs";
|
|
26443
|
-
import { mkdir as mkdir5, open as open2, readFile as readFile8, rename as
|
|
26572
|
+
import { mkdir as mkdir5, open as open2, readFile as readFile8, rename as rename3, writeFile as writeFile7, unlink as unlink3 } from "node:fs/promises";
|
|
26444
26573
|
import net2 from "node:net";
|
|
26445
26574
|
import os6 from "node:os";
|
|
26446
26575
|
import path14 from "node:path";
|
|
@@ -31828,7 +31957,7 @@ var AntigravityTransport = class {
|
|
|
31828
31957
|
if (!this.conversationId) throw new Error("Antigravity session is not open");
|
|
31829
31958
|
if (!this.child) await this.startProcess({ ...this.config, threadId: this.conversationId });
|
|
31830
31959
|
if (this.activeTurn) throw new Error("Antigravity already has an active turn");
|
|
31831
|
-
const turnId =
|
|
31960
|
+
const turnId = randomUUID7();
|
|
31832
31961
|
this.activeTurn = turnId;
|
|
31833
31962
|
this.messageText.clear();
|
|
31834
31963
|
const parts = (params.input ?? []).filter((part) => typeof part?.text === "string").map((part) => part.text);
|
|
@@ -31974,7 +32103,7 @@ ${promptText}`;
|
|
|
31974
32103
|
return;
|
|
31975
32104
|
}
|
|
31976
32105
|
if (frame.event === "init") {
|
|
31977
|
-
this.conversationId = frame.conversation_id || frame.init?.conversation_id ||
|
|
32106
|
+
this.conversationId = frame.conversation_id || frame.init?.conversation_id || randomUUID7();
|
|
31978
32107
|
if (this.resolveReady) {
|
|
31979
32108
|
this.resolveReady();
|
|
31980
32109
|
this.resolveReady = void 0;
|
|
@@ -32134,7 +32263,7 @@ ${promptText}`;
|
|
|
32134
32263
|
}
|
|
32135
32264
|
}
|
|
32136
32265
|
if (!lock) throw new Error(`Antigravity MCP configuration is locked: ${lockFile}`);
|
|
32137
|
-
const temporary = `${configFile}.${
|
|
32266
|
+
const temporary = `${configFile}.${randomUUID7()}.tmp`;
|
|
32138
32267
|
try {
|
|
32139
32268
|
let text2 = "{}";
|
|
32140
32269
|
try {
|
|
@@ -32154,8 +32283,8 @@ ${promptText}`;
|
|
|
32154
32283
|
const launcher = { command: process.execPath, args: ["-e", BRIDGE2], ...process.versions.electron ? { env: { ELECTRON_RUN_AS_NODE: "1" } } : {} };
|
|
32155
32284
|
if (JSON.stringify(existing) === JSON.stringify(launcher)) return;
|
|
32156
32285
|
config2.mcpServers.alp = launcher;
|
|
32157
|
-
await
|
|
32158
|
-
await
|
|
32286
|
+
await writeFile7(temporary, JSON.stringify(config2, null, 2) + "\n", { mode: 384 });
|
|
32287
|
+
await rename3(temporary, configFile);
|
|
32159
32288
|
} finally {
|
|
32160
32289
|
await unlink3(temporary).catch(() => {
|
|
32161
32290
|
});
|
|
@@ -32209,7 +32338,7 @@ ${promptText}`;
|
|
|
32209
32338
|
}
|
|
32210
32339
|
async toolCall(tool, args) {
|
|
32211
32340
|
if (this.activeTurn && this.external?.has(tool)) {
|
|
32212
|
-
const callId2 = `agy-mcp-tool-${
|
|
32341
|
+
const callId2 = `agy-mcp-tool-${randomUUID7()}`;
|
|
32213
32342
|
const item2 = { type: "dynamicToolCall", id: callId2, callId: callId2, tool, arguments: args };
|
|
32214
32343
|
this.emit("item/started", { threadId: this.conversationId, item: { ...item2, status: "inProgress" } });
|
|
32215
32344
|
const result2 = await this.external.call(tool, args).catch(() => ({ content: [{ type: "text", text: `External MCP tool '${tool}' failed` }], isError: true }));
|
|
@@ -32226,7 +32355,7 @@ ${promptText}`;
|
|
|
32226
32355
|
isError: true
|
|
32227
32356
|
};
|
|
32228
32357
|
}
|
|
32229
|
-
const callId = `agy-alp-tool-${
|
|
32358
|
+
const callId = `agy-alp-tool-${randomUUID7()}`;
|
|
32230
32359
|
const item = { type: "dynamicToolCall", id: callId, callId, tool, arguments: args };
|
|
32231
32360
|
this.emit("item/started", { threadId: this.conversationId, item: { ...item, status: "inProgress" } });
|
|
32232
32361
|
const result = await this.requestHandler("item/tool/call", {
|
|
@@ -32391,12 +32520,12 @@ function shippedTemplateRoot() {
|
|
|
32391
32520
|
}
|
|
32392
32521
|
|
|
32393
32522
|
// packages/core/src/resolver.js
|
|
32394
|
-
import { readFile as readFile10, readdir as
|
|
32523
|
+
import { readFile as readFile10, readdir as readdir6, stat as stat4 } from "node:fs/promises";
|
|
32395
32524
|
import path16 from "node:path";
|
|
32396
32525
|
|
|
32397
32526
|
// packages/core/src/library.js
|
|
32398
|
-
import { mkdir as mkdir6, readFile as readFile9, readdir as
|
|
32399
|
-
import { createHash as createHash6, randomUUID as
|
|
32527
|
+
import { mkdir as mkdir6, readFile as readFile9, readdir as readdir5, writeFile as writeFile8 } from "node:fs/promises";
|
|
32528
|
+
import { createHash as createHash6, randomUUID as randomUUID8 } from "node:crypto";
|
|
32400
32529
|
import path15 from "node:path";
|
|
32401
32530
|
var MANIFEST = "library.json";
|
|
32402
32531
|
var sha = (text2) => createHash6("sha256").update(text2.replaceAll("\r\n", "\n")).digest("hex");
|
|
@@ -32413,7 +32542,7 @@ async function shippedFiles({ templateRoot: templateRoot2, templates }) {
|
|
|
32413
32542
|
const root = templateRoot2 ?? shippedTemplateRoot();
|
|
32414
32543
|
const files = { "role-skills.json": await readFile9(new URL("role-skills.json", root), "utf8") };
|
|
32415
32544
|
async function walk(relative) {
|
|
32416
|
-
for (const entry of await
|
|
32545
|
+
for (const entry of await readdir5(new URL(`${relative}/`, root), { withFileTypes: true })) {
|
|
32417
32546
|
if (entry.isDirectory()) await walk(`${relative}/${entry.name}`);
|
|
32418
32547
|
else if (entry.isFile()) files[`${relative}/${entry.name}`] = await readFile9(new URL(`${relative}/${entry.name}`, root), "utf8");
|
|
32419
32548
|
}
|
|
@@ -32437,21 +32566,21 @@ async function seedLibrary(home2, options2 = {}) {
|
|
|
32437
32566
|
if (seeded === void 0) {
|
|
32438
32567
|
if (current !== void 0) continue;
|
|
32439
32568
|
await mkdir6(path15.dirname(target), { recursive: true });
|
|
32440
|
-
await
|
|
32569
|
+
await writeFile8(target, content, { flag: "wx" }).catch((error51) => {
|
|
32441
32570
|
if (error51.code !== "EEXIST") throw error51;
|
|
32442
32571
|
});
|
|
32443
32572
|
manifest.files[name2] = sha(content);
|
|
32444
32573
|
created.push(name2);
|
|
32445
32574
|
} else if (current !== void 0 && sha(current) === seeded && seeded !== sha(content)) {
|
|
32446
|
-
await
|
|
32575
|
+
await writeFile8(target, content);
|
|
32447
32576
|
manifest.files[name2] = sha(content);
|
|
32448
32577
|
updated.push(name2);
|
|
32449
32578
|
}
|
|
32450
32579
|
}
|
|
32451
32580
|
if (created.length || updated.length || manifestText === void 0) {
|
|
32452
32581
|
await mkdir6(home2, { recursive: true });
|
|
32453
|
-
const temporary = `${manifestPath}.${
|
|
32454
|
-
await
|
|
32582
|
+
const temporary = `${manifestPath}.${randomUUID8().slice(0, 8)}.tmp`;
|
|
32583
|
+
await writeFile8(temporary, JSON.stringify(manifest, null, 2) + "\n");
|
|
32455
32584
|
await moveFile(temporary, manifestPath);
|
|
32456
32585
|
}
|
|
32457
32586
|
return { created, updated };
|
|
@@ -32512,7 +32641,7 @@ async function jsonObject(file3, code) {
|
|
|
32512
32641
|
}
|
|
32513
32642
|
async function entries(directory) {
|
|
32514
32643
|
try {
|
|
32515
|
-
return (await
|
|
32644
|
+
return (await readdir6(directory, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
|
|
32516
32645
|
} catch (error51) {
|
|
32517
32646
|
if (error51.code === "ENOENT") return [];
|
|
32518
32647
|
throw error51;
|
|
@@ -32718,7 +32847,7 @@ import os7 from "node:os";
|
|
|
32718
32847
|
import { createHash as createHash7 } from "node:crypto";
|
|
32719
32848
|
|
|
32720
32849
|
// packages/core/src/teams.js
|
|
32721
|
-
import { readdir as
|
|
32850
|
+
import { readdir as readdir7 } from "node:fs/promises";
|
|
32722
32851
|
import path17 from "node:path";
|
|
32723
32852
|
var BUILTIN_TEAMS = ["pho", "cafe"];
|
|
32724
32853
|
var TEAM_SETTINGS = ["$schema", "label", "description", "main", "members", "delegation", "maxPeers", "supervisor", "oracleModels", "oracleThinking", "modelSetup"];
|
|
@@ -32797,7 +32926,7 @@ function validateTeam(config2, source) {
|
|
|
32797
32926
|
}
|
|
32798
32927
|
async function directories(directory) {
|
|
32799
32928
|
try {
|
|
32800
|
-
return (await
|
|
32929
|
+
return (await readdir7(directory, { withFileTypes: true })).filter((entry) => entry.isDirectory() && validName2(entry.name)).map((entry) => entry.name).sort();
|
|
32801
32930
|
} catch (error51) {
|
|
32802
32931
|
if (error51.code === "ENOENT") return [];
|
|
32803
32932
|
throw error51;
|
|
@@ -32980,9 +33109,9 @@ async function resolveWorkflow(root, selected, restored, { library, templates, s
|
|
|
32980
33109
|
}
|
|
32981
33110
|
|
|
32982
33111
|
// packages/core/src/init.js
|
|
32983
|
-
import { mkdir as mkdir7, writeFile as
|
|
33112
|
+
import { mkdir as mkdir7, writeFile as writeFile9, lstat as lstat4, readFile as readFile11, rename as rename4, rm as rm3 } from "node:fs/promises";
|
|
32984
33113
|
import path20 from "node:path";
|
|
32985
|
-
import { randomUUID as
|
|
33114
|
+
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
32986
33115
|
async function initProject(projectRoot, { templateRoot: templateRoot2, templates, library, syncProfiles = true, onlyManagedProfiles = false, teamIds } = {}) {
|
|
32987
33116
|
const root = path20.resolve(projectRoot);
|
|
32988
33117
|
const sourceRoot = () => templateRoot2 ?? shippedTemplateRoot();
|
|
@@ -33014,7 +33143,7 @@ async function initProject(projectRoot, { templateRoot: templateRoot2, templates
|
|
|
33014
33143
|
for (const [name2, content] of Object.entries(files)) {
|
|
33015
33144
|
const target = path20.join(root, name2);
|
|
33016
33145
|
try {
|
|
33017
|
-
await
|
|
33146
|
+
await writeFile9(target, content, { flag: "wx" });
|
|
33018
33147
|
created.push(name2);
|
|
33019
33148
|
} catch (error51) {
|
|
33020
33149
|
if (error51.code !== "EEXIST") throw error51;
|
|
@@ -33028,7 +33157,7 @@ async function initProject(projectRoot, { templateRoot: templateRoot2, templates
|
|
|
33028
33157
|
validateSettings(next, target);
|
|
33029
33158
|
if (JSON.stringify(next) !== JSON.stringify(settings)) {
|
|
33030
33159
|
if (await readFile11(target, "utf8") !== before) throw new Error(`File changed during initialization: ${target}`);
|
|
33031
|
-
await
|
|
33160
|
+
await writeFile9(target, JSON.stringify(next, null, 2) + "\n");
|
|
33032
33161
|
updated.push(name2);
|
|
33033
33162
|
}
|
|
33034
33163
|
}
|
|
@@ -33050,11 +33179,11 @@ async function syncProjectSettings(projectRoot, { library, templates, teamIds, r
|
|
|
33050
33179
|
const next = await syncTemplateProfiles(settings, { root, library, templates, teamIds, replaceModels });
|
|
33051
33180
|
validateSettings(next, file3);
|
|
33052
33181
|
if (JSON.stringify(settings) === JSON.stringify(next)) return false;
|
|
33053
|
-
const temporary = `${file3}.${
|
|
33182
|
+
const temporary = `${file3}.${randomUUID9()}.tmp`;
|
|
33054
33183
|
try {
|
|
33055
|
-
await
|
|
33184
|
+
await writeFile9(temporary, JSON.stringify(next, null, 2) + "\n", { flag: "wx", mode: info.mode });
|
|
33056
33185
|
if (await readFile11(file3, "utf8") !== before) throw new Error(`Settings changed while saving the template: ${file3}`);
|
|
33057
|
-
await
|
|
33186
|
+
await rename4(temporary, file3);
|
|
33058
33187
|
} finally {
|
|
33059
33188
|
await rm3(temporary, { force: true });
|
|
33060
33189
|
}
|
|
@@ -33113,8 +33242,8 @@ async function compileAgent(adapter, agent) {
|
|
|
33113
33242
|
}
|
|
33114
33243
|
|
|
33115
33244
|
// packages/core/src/user-settings.js
|
|
33116
|
-
import { mkdir as mkdir8, readFile as readFile13, writeFile as
|
|
33117
|
-
import { randomUUID as
|
|
33245
|
+
import { mkdir as mkdir8, readFile as readFile13, writeFile as writeFile10 } from "node:fs/promises";
|
|
33246
|
+
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
33118
33247
|
import path22 from "node:path";
|
|
33119
33248
|
var DEFAULT_LANGUAGE = "Vietnamese";
|
|
33120
33249
|
var LANGUAGE_CHARS = 40;
|
|
@@ -33158,8 +33287,8 @@ async function setLanguage(home2, language) {
|
|
|
33158
33287
|
else next.language = language.trim();
|
|
33159
33288
|
validateUserSettings(next, file3);
|
|
33160
33289
|
await mkdir8(home2, { recursive: true, mode: 448 });
|
|
33161
|
-
const temporary = `${file3}.${
|
|
33162
|
-
await
|
|
33290
|
+
const temporary = `${file3}.${randomUUID10().slice(0, 8)}.tmp`;
|
|
33291
|
+
await writeFile10(temporary, JSON.stringify(next, null, 2) + "\n");
|
|
33163
33292
|
await moveFile(temporary, file3);
|
|
33164
33293
|
return languageSetting(home2);
|
|
33165
33294
|
}
|
|
@@ -33382,7 +33511,7 @@ ${project}` : "");
|
|
|
33382
33511
|
}
|
|
33383
33512
|
|
|
33384
33513
|
// packages/runtime/src/recall.ts
|
|
33385
|
-
import { mkdir as mkdir9, readFile as readFile15, rename as
|
|
33514
|
+
import { mkdir as mkdir9, readFile as readFile15, rename as rename5, writeFile as writeFile11 } from "node:fs/promises";
|
|
33386
33515
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
33387
33516
|
import path24 from "node:path";
|
|
33388
33517
|
var RECALL_KEEP_MS = 14 * 24 * 60 * 60 * 1e3;
|
|
@@ -33400,8 +33529,8 @@ function createRecallBook(file3) {
|
|
|
33400
33529
|
writes2 = writes2.then(async () => {
|
|
33401
33530
|
await mkdir9(path24.dirname(file3), { recursive: true, mode: 448 });
|
|
33402
33531
|
const temporary = `${file3}.${process.pid}.${randomBytes4(4).toString("hex")}.tmp`;
|
|
33403
|
-
await
|
|
33404
|
-
await
|
|
33532
|
+
await writeFile11(temporary, body, { mode: 384 });
|
|
33533
|
+
await rename5(temporary, file3);
|
|
33405
33534
|
}).catch(() => {
|
|
33406
33535
|
});
|
|
33407
33536
|
return writes2;
|
|
@@ -33559,7 +33688,7 @@ var HANDOFF_TOOL = {
|
|
|
33559
33688
|
var WAIT_TOOL = {
|
|
33560
33689
|
type: "function",
|
|
33561
33690
|
name: "alp_wait",
|
|
33562
|
-
description: "Wait for mail from your assignments: results, questions, notes, and stall reports. Returns as soon as any arrives, or with an empty list and a status snapshot
|
|
33691
|
+
description: "Wait for mail from your assignments: results, questions, notes, and stall reports. Returns as soon as any arrives, a check-in comes or the user writes to you; otherwise with an empty list and a status snapshot after timeoutMs (default 5 minutes, at most 15). Ending your turn instead never cancels your assignments.",
|
|
33563
33692
|
inputSchema: {
|
|
33564
33693
|
type: "object",
|
|
33565
33694
|
properties: {
|
|
@@ -33691,7 +33820,7 @@ var UNPIN_TOOL = {
|
|
|
33691
33820
|
var ASK_TOOL = {
|
|
33692
33821
|
type: "function",
|
|
33693
33822
|
name: "alp_ask",
|
|
33694
|
-
description: "Ask a question and wait for the answer: your requester, or the user. Only main talks to the user, unless the user has written to you.
|
|
33823
|
+
description: "Ask a question and wait for the answer: your requester, or the user. Only main talks to the user, unless the user has written to you. Holds your turn until answered; returns unanswered after the ask timeout (15 minutes for a requester, 30 for the user), and user_wrote when the user writes in the chat instead of answering.",
|
|
33695
33824
|
inputSchema: {
|
|
33696
33825
|
type: "object",
|
|
33697
33826
|
properties: {
|
|
@@ -33720,7 +33849,7 @@ var LESSON_TOOL = {
|
|
|
33720
33849
|
var SKILL_TOOL = {
|
|
33721
33850
|
type: "function",
|
|
33722
33851
|
name: "alp_skill",
|
|
33723
|
-
description: "Propose a skill distilled from your lessons. ALP shows the whole skill to the user, and saves it to the user's skill library and gives it to the roles only when the user approves;
|
|
33852
|
+
description: "Propose a skill distilled from your lessons. ALP shows the whole skill to the user, and saves it to the user's skill library and gives it to the roles only when the user approves. Returns pending_approval with approvalId immediately; the decision (saved, or not saved with the user's feedback) arrives once by mail, even after your turn ends. Continue other work. A retry of the same draft returns its existing pending/result in this session; use a fresh requestId only to propose an identical draft again.",
|
|
33724
33853
|
inputSchema: {
|
|
33725
33854
|
type: "object",
|
|
33726
33855
|
properties: {
|
|
@@ -33729,7 +33858,8 @@ var SKILL_TOOL = {
|
|
|
33729
33858
|
body: { type: "string", description: "The SKILL.md body in Markdown, without frontmatter: when to use it, the method as steps, and the checks." },
|
|
33730
33859
|
roles: { type: "array", items: { type: "string" }, description: "The roles whose work the skill guides, and only those: any of main (yourself), lead, peer, oracle, reviewer, supervisor, or a custom agent of this project." },
|
|
33731
33860
|
lessons: { type: "array", items: { type: "string" }, description: "The exact text of the lessons this skill replaces; they leave the lessons files once it is saved." },
|
|
33732
|
-
replace: { type: "boolean", description: "Propose a new version of an existing skill of this name." }
|
|
33861
|
+
replace: { type: "boolean", description: "Propose a new version of an existing skill of this name." },
|
|
33862
|
+
requestId: { type: "string", description: "Optional session-scoped idempotency key (1-100 characters). Reuse with the same draft on retries; a fresh key explicitly proposes a new operation." }
|
|
33733
33863
|
},
|
|
33734
33864
|
required: ["name", "description", "body", "roles"],
|
|
33735
33865
|
additionalProperties: false
|
|
@@ -33738,7 +33868,7 @@ var SKILL_TOOL = {
|
|
|
33738
33868
|
var ISSUE_TOOL = {
|
|
33739
33869
|
type: "function",
|
|
33740
33870
|
name: "alp_issue",
|
|
33741
|
-
description: "GitHub issues of this project (target project, from its origin remote) or of ALP itself (target alp). search needs no approval. create and comment show the whole draft to the user and post it only when the user approves;
|
|
33871
|
+
description: "GitHub issues of this project (target project, from its origin remote) or of ALP itself (target alp). search needs no approval. create and comment show the whole draft to the user and post it only when the user approves; create/comment return pending_approval with approvalId immediately, then send one decision/posting result by mail, even after your turn ends. Continue other work. Draft retries return the existing pending/result in this session. Use a fresh requestId only to intentionally propose an identical draft again; reuse one requestId with the exact draft to replay safely.",
|
|
33742
33872
|
inputSchema: {
|
|
33743
33873
|
type: "object",
|
|
33744
33874
|
properties: {
|
|
@@ -33748,6 +33878,7 @@ var ISSUE_TOOL = {
|
|
|
33748
33878
|
title: { type: "string", description: "For create." },
|
|
33749
33879
|
body: { type: "string", description: "For create and comment: Markdown. Facts, reproduction and evidence; no secrets." },
|
|
33750
33880
|
issue: { type: "integer", description: "For comment: the issue number." },
|
|
33881
|
+
requestId: { type: "string", description: "Optional session-scoped idempotency key (1-100 characters). Reuse with the same draft on retries; a fresh key explicitly proposes a new operation." },
|
|
33751
33882
|
labels: { type: "array", items: { type: "string" }, description: "For create: existing labels of the repository." }
|
|
33752
33883
|
},
|
|
33753
33884
|
required: ["action", "target"],
|
|
@@ -34181,7 +34312,7 @@ function nativeSessionConfig(runtimeKind, mapping, targets, parentAgent, role, s
|
|
|
34181
34312
|
dynamicTools: [SEND_TOOL, BOARD_TOOL, taskTool(taskActions(mapping, parentAgent, role))]
|
|
34182
34313
|
};
|
|
34183
34314
|
}
|
|
34184
|
-
const delegationInstruction = targets.length ? `Use alp_delegate to assign bounded work to: ${targets.join(", ")}. Work runs in the background: by default alp_delegate starts the assignment and returns its assignmentId at once, and its result, questions and stall reports come to you as mail. Pass wait: true only when your very next step cannot go on without the result; it then returns the result, or early with the child's first question. A result carries the child's structured handoff (null if it filed none) and output, its final message. While assignments run, go on with work that does not depend on them; when only their results are left, alp_wait for them (mail from your requester or the user ends the wait early), or end your turn and ALP wakes you with their results and questions. Answer questions with alp_send kind answer and replyTo; use kind steer to change an instruction, note for information. To stop an assignment, for example one parked on a usage limit whose work should go on now, use alp_cancel and delegate the rest, on another runtime (pass a model of claude: or codex:); its changes stay in the checkout or its worktree. At most ${mapping.workflow.maxPeers} peers may run concurrently. Concurrent peers must be read-only or isolated: pass isolation "worktree" to give a writing peer its own git worktree. A worktree result lists its branch and changed files; apply it with alp_merge (uncommitted, conflicts left as markers) or drop it with alp_discard, then verify. Writers in this shared checkout run one at a time. To learn why a finished assignment did something, ask it with alp_recall rather than guessing from its handoff. When the project configures verify commands, ALP runs them before alp_merge applies a change, and after a writer in your checkout finishes; a failed check applies nothing. To fix a failed worktree change, delegate again with continueFrom set to its assignmentId. Do not run shell/file mutations in parallel with delegation. Include scope, constraints, verification, and required handoff in task. Child inherits your mode unless you request read-only. Review its returned evidence before answering. Do not use native spawn tools or shell-launched agents to bypass this route.` : "No delegation targets are authorized. Do not spawn agents or use shell-launched agents. Complete your assigned scope and return evidence.";
|
|
34315
|
+
const delegationInstruction = targets.length ? `Use alp_delegate to assign bounded work to: ${targets.join(", ")}. Delegation has two finite budgets shared by the entire root session tree: 16 implementation starts (lead/peer) and separately 16 read-only advisor starts (reviewer/oracle). Only a new accepted user prompt in root, including steering, resets both; child turns, mail wakes and approval answers do not. Started assignments count even when they fail. Work runs in the background: by default alp_delegate starts the assignment and returns its assignmentId at once, and its result, questions and stall reports come to you as mail. Pass wait: true only when your very next step cannot go on without the result; it then returns the result, or early with the child's first question. A result carries the child's structured handoff (null if it filed none) and output, its final message. While assignments run, go on with work that does not depend on them; when only their results are left, alp_wait for them (mail from your requester or the user ends the wait early), or end your turn and ALP wakes you with their results and questions. Answer questions with alp_send kind answer and replyTo; use kind steer to change an instruction, note for information. To stop an assignment, for example one parked on a usage limit whose work should go on now, use alp_cancel and delegate the rest, on another runtime (pass a model of claude: or codex:); its changes stay in the checkout or its worktree. At most ${mapping.workflow.maxPeers} peers may run concurrently. Concurrent peers must be read-only or isolated: pass isolation "worktree" to give a writing peer its own git worktree. A worktree result lists its branch and changed files; apply it with alp_merge (uncommitted, conflicts left as markers) or drop it with alp_discard, then verify. Writers in this shared checkout run one at a time. To learn why a finished assignment did something, ask it with alp_recall rather than guessing from its handoff. When the project configures verify commands, ALP runs them before alp_merge applies a change, and after a writer in your checkout finishes; a failed check applies nothing. To fix a failed worktree change, delegate again with continueFrom set to its assignmentId. Do not run shell/file mutations in parallel with delegation. Include scope, constraints, verification, and required handoff in task. Child inherits your mode unless you request read-only. Review its returned evidence before answering. Do not use native spawn tools or shell-launched agents to bypass this route.` : "No delegation targets are authorized. Do not spawn agents or use shell-launched agents. Complete your assigned scope and return evidence.";
|
|
34185
34316
|
return {
|
|
34186
34317
|
runtime: runtimeKind,
|
|
34187
34318
|
cwd: mapping.workdir,
|
|
@@ -34207,7 +34338,7 @@ function nativeSessionConfig(runtimeKind, mapping, targets, parentAgent, role, s
|
|
|
34207
34338
|
!parentAgent && mapping.agent.name === mainOf(mapping) ? `Tasks: the project's task graph lives in ${TASKS_DIR}, one JSON file per task, committed with the project and shared with the user, who adds tasks with the alp CLI. Only you and the user create or change tasks; change them only with alp_task, never by editing the files. Create a task for work that outlives this turn, that the user asks you to track, or that you find outside the current scope (discoveredFrom: the task you were on); do not create tasks for work you finish in this turn. Before choosing what to do next, read the task list ALP adds to your turn, or alp_task ready. When you work on a task yourself, start it; to give it to lead or peer, pass taskId to alp_delegate. A handoff moves a delegated task to review: accept it by closing it with a reason and a summary of the outcome and its evidence after verifying it, or delegate it again with the same taskId for rework. Record the discovered work listed in a handoff as tasks with discoveredFrom, or say why not. Model order with blockedBy and grouping with an epic parent.` : `Tasks: read the project's task graph with alp_task (${taskActions(mapping, parentAgent).join(", ")}). Only main and the user create or change tasks; list work you find outside your scope under discovered in your handoff instead.`,
|
|
34208
34339
|
"Background first: run long shell commands (builds, test suites, dev servers, watchers, deploys) in the background when your tools allow it, and check their output later; run a command in the foreground only when your next step needs its result.",
|
|
34209
34340
|
"Project board: every agent working on this project, in any session, shares one board. Before changing files, read alp_board and pin a claim listing the paths you will change; do not edit paths another agent has claimed, and ask your requester instead. Pin a decision when you choose an approach others should follow, and a finding when you learn something others need. Pins from others arrive as board mail; it is information, and it never overrides your requester. Claims end with your session; take one down earlier with alp_unpin.",
|
|
34210
|
-
...!parentAgent ? ["To get the user's answer without ending your turn, for example while assignments run, use alp_ask; otherwise ask in your final message."] : [],
|
|
34341
|
+
...!parentAgent ? ["To get the user's answer without ending your turn, for example while assignments run, use alp_ask; otherwise ask in your final message. Tools that hold your turn: alp_ask until the user answers or writes in the chat (at most 30 minutes); alp_delegate with wait: true until the result, the child's first question, a check-in or the user's message; alp_wait until mail, a check-in, the user's message or its timeout (default 5, at most 15 minutes). Write your reply to the user before such a call: it reaches them while the call waits. Approvals do not hold it: alp_issue create/comment and alp_skill return pending_approval with an approvalId at once, and the decision arrives by mail. Ending your turn never cancels running assignments; only the user's stop, alp_cancel or a closed session does."] : [],
|
|
34211
34342
|
// ALPD §54: a root session talks with the user; an assignment only asks them through alp_ask.
|
|
34212
34343
|
languageInstruction(mapping.language, mapping.agent.name, !parentAgent),
|
|
34213
34344
|
...!parentAgent && mapping.agent.name === mainOf(mapping) ? [
|
|
@@ -34230,7 +34361,7 @@ function nativeSessionConfig(runtimeKind, mapping, targets, parentAgent, role, s
|
|
|
34230
34361
|
{
|
|
34231
34362
|
type: "function",
|
|
34232
34363
|
name: "alp_delegate",
|
|
34233
|
-
description: "Delegate a bounded task to an authorized ALP agent and wait
|
|
34364
|
+
description: "Delegate a bounded task to an authorized ALP agent. Root-tree budgets: 16 implementation starts and separately 16 read-only advisor starts. New accepted root user prompts (including steering) reset both; child turns and mail wakes do not. The default returns immediately; wait true waits for handoff.",
|
|
34234
34365
|
inputSchema: {
|
|
34235
34366
|
type: "object",
|
|
34236
34367
|
properties: {
|
|
@@ -34277,9 +34408,9 @@ function nativeSessionConfig(runtimeKind, mapping, targets, parentAgent, role, s
|
|
|
34277
34408
|
}
|
|
34278
34409
|
|
|
34279
34410
|
// packages/runtime/src/runtime.ts
|
|
34280
|
-
import { createHash as createHash8, randomUUID as
|
|
34411
|
+
import { createHash as createHash8, randomUUID as randomUUID12 } from "node:crypto";
|
|
34281
34412
|
import { readFileSync as readFileSync3, realpathSync as realpathSync5 } from "node:fs";
|
|
34282
|
-
import { appendFile as appendFile2, mkdir as mkdir12, open as open3, readFile as readFile18, realpath as realpath4, rm as rm4, stat as stat6, writeFile as
|
|
34413
|
+
import { appendFile as appendFile2, mkdir as mkdir12, open as open3, readFile as readFile18, realpath as realpath4, rm as rm4, stat as stat6, writeFile as writeFile14 } from "node:fs/promises";
|
|
34283
34414
|
import os8 from "node:os";
|
|
34284
34415
|
import path30 from "node:path";
|
|
34285
34416
|
|
|
@@ -34355,8 +34486,8 @@ function runHook(hook, payload, { cwd, env = {}, timeoutMs } = {}) {
|
|
|
34355
34486
|
}
|
|
34356
34487
|
|
|
34357
34488
|
// packages/core/src/trust.js
|
|
34358
|
-
import { mkdir as mkdir10, readFile as readFile16, realpath as realpath3, writeFile as
|
|
34359
|
-
import { randomUUID as
|
|
34489
|
+
import { mkdir as mkdir10, readFile as readFile16, realpath as realpath3, writeFile as writeFile12 } from "node:fs/promises";
|
|
34490
|
+
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
34360
34491
|
import path27 from "node:path";
|
|
34361
34492
|
var file2 = (home2) => path27.join(home2, "state", "trust.json");
|
|
34362
34493
|
var key = (root) => realpath3(path27.resolve(root)).catch(() => path27.resolve(root));
|
|
@@ -34371,8 +34502,8 @@ async function load(home2) {
|
|
|
34371
34502
|
}
|
|
34372
34503
|
async function store(home2, value) {
|
|
34373
34504
|
await mkdir10(path27.dirname(file2(home2)), { recursive: true });
|
|
34374
|
-
const temporary = `${file2(home2)}.${
|
|
34375
|
-
await
|
|
34505
|
+
const temporary = `${file2(home2)}.${randomUUID11().slice(0, 8)}.tmp`;
|
|
34506
|
+
await writeFile12(temporary, JSON.stringify(value, null, 2) + "\n");
|
|
34376
34507
|
await moveFile(temporary, file2(home2));
|
|
34377
34508
|
}
|
|
34378
34509
|
async function isTrusted(home2, projectRoot) {
|
|
@@ -34415,7 +34546,7 @@ ${event.body ?? JSON.stringify(event.result)}`;
|
|
|
34415
34546
|
|
|
34416
34547
|
// packages/runtime/src/live.ts
|
|
34417
34548
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
34418
|
-
import { mkdir as mkdir11, rename as
|
|
34549
|
+
import { mkdir as mkdir11, rename as rename6, writeFile as writeFile13 } from "node:fs/promises";
|
|
34419
34550
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
34420
34551
|
import path28 from "node:path";
|
|
34421
34552
|
var valid2 = (entry) => !!entry && typeof entry === "object" && ["assignmentId", "rootId", "parentId", "agent", "project", "runtime", "model", "threadId", "mode", "isolation", "epoch"].every((key2) => typeof entry[key2] === "string") && Array.isArray(entry.ancestry) && !!entry.spec && typeof entry.spec === "object" && typeof entry.startedAt === "number";
|
|
@@ -34434,8 +34565,8 @@ function createLiveBook(file3) {
|
|
|
34434
34565
|
writes2 = writes2.then(async () => {
|
|
34435
34566
|
await mkdir11(path28.dirname(file3), { recursive: true, mode: 448 });
|
|
34436
34567
|
const temporary = `${file3}.${process.pid}.${randomBytes5(4).toString("hex")}.tmp`;
|
|
34437
|
-
await
|
|
34438
|
-
await
|
|
34568
|
+
await writeFile13(temporary, body, { mode: 384 });
|
|
34569
|
+
await rename6(temporary, file3);
|
|
34439
34570
|
}).catch((error51) => createLogger({ component: "runtime" }).error("could not save the live assignments", { code: "ALP_PERSISTENCE_WRITE_FAILED", operation: "live assignments", error: error51 }));
|
|
34440
34571
|
return writes2;
|
|
34441
34572
|
};
|
|
@@ -35332,7 +35463,7 @@ function parse3(toml, options2 = {}) {
|
|
|
35332
35463
|
var HAS_WELLFORMED = !!"".isWellFormed;
|
|
35333
35464
|
|
|
35334
35465
|
// packages/core/src/formulas.js
|
|
35335
|
-
import { readdir as
|
|
35466
|
+
import { readdir as readdir8, readFile as readFile17 } from "node:fs/promises";
|
|
35336
35467
|
import path29 from "node:path";
|
|
35337
35468
|
var fail4 = (message) => {
|
|
35338
35469
|
throw new AlpError("INVALID_FORMULA", message);
|
|
@@ -35354,7 +35485,7 @@ async function listFormulas(projectRoot, home2, { toml } = {}) {
|
|
|
35354
35485
|
for (const dir of formulaDirs(projectRoot, home2)) {
|
|
35355
35486
|
let names;
|
|
35356
35487
|
try {
|
|
35357
|
-
names = await
|
|
35488
|
+
names = await readdir8(dir);
|
|
35358
35489
|
} catch (error51) {
|
|
35359
35490
|
if (error51.code === "ENOENT") continue;
|
|
35360
35491
|
throw error51;
|
|
@@ -35480,7 +35611,7 @@ async function pourFormula(projectRoot, formula, given, by, { dryRun = false, pa
|
|
|
35480
35611
|
title: fill(step.title, values, `Step ${step.id}`),
|
|
35481
35612
|
...step.description ? { description: fill(step.description, values, `Step ${step.id}`) } : {}
|
|
35482
35613
|
}));
|
|
35483
|
-
return batch(projectRoot, ({ add, link }) => {
|
|
35614
|
+
return batch(projectRoot, ({ add, link: link2 }) => {
|
|
35484
35615
|
const epic = add({
|
|
35485
35616
|
title: title.slice(0, 200),
|
|
35486
35617
|
type: "epic",
|
|
@@ -35513,7 +35644,7 @@ async function pourFormula(projectRoot, formula, given, by, { dryRun = false, pa
|
|
|
35513
35644
|
}
|
|
35514
35645
|
for (const step of steps) {
|
|
35515
35646
|
for (const need of step.needs) {
|
|
35516
|
-
const refused =
|
|
35647
|
+
const refused = link2(made.get(step.id), "blockedBy", made.get(need).id);
|
|
35517
35648
|
if (refused) fail4(`Step ${step.id} cannot need ${need}: ${refused}`);
|
|
35518
35649
|
}
|
|
35519
35650
|
}
|
|
@@ -35525,6 +35656,7 @@ async function pourFormula(projectRoot, formula, given, by, { dryRun = false, pa
|
|
|
35525
35656
|
var MAX_WAKES = 8;
|
|
35526
35657
|
var USER_REPLY_MS = 6e4;
|
|
35527
35658
|
var USER_WROTE = "The user just wrote to you; their message follows. Answer them first in a short message they can read (tool calls alone show them nothing), and steer an assignment with alp_send when their words change its brief. Your assignments keep running: then alp_wait again, or end your turn and ALP wakes you with their results.";
|
|
35659
|
+
var TURN_ENDED = "Turn ended before the result arrived. Running assignments keep running; ALP wakes you with their results as mail.";
|
|
35528
35660
|
var CHECKED_IN = "Not a result: the assignment keeps running. Act on this mail first (a steer from your requester or the user's words may change what your assignments should do: pass that on with alp_send), then alp_wait again or end your turn.";
|
|
35529
35661
|
var RESTART_LIMIT = 3;
|
|
35530
35662
|
var RESTART_WINDOW_MS = 10 * 6e4;
|
|
@@ -35560,7 +35692,7 @@ function createAlpRuntime(options2 = {}) {
|
|
|
35560
35692
|
const wordsFor = (session) => words(session.mapping.language);
|
|
35561
35693
|
const childContexts = /* @__PURE__ */ new Map();
|
|
35562
35694
|
const listeners = /* @__PURE__ */ new Set();
|
|
35563
|
-
const epoch =
|
|
35695
|
+
const epoch = randomUUID12();
|
|
35564
35696
|
const recalls = createRecallBook(options2.recallFile);
|
|
35565
35697
|
const inFlight = createLiveBook(options2.liveFile);
|
|
35566
35698
|
let inherited = inFlight.all().filter((entry) => entry.epoch !== epoch);
|
|
@@ -35617,11 +35749,12 @@ function createAlpRuntime(options2 = {}) {
|
|
|
35617
35749
|
...session.parent ? { parentId: session.parent } : {},
|
|
35618
35750
|
...session.toolCallId ? { toolCallId: session.toolCallId } : {},
|
|
35619
35751
|
...session.active ? { activeTurnId: session.active } : {},
|
|
35620
|
-
|
|
35752
|
+
// turn.ended listeners persist the intent before terminal() finishes parking it.
|
|
35753
|
+
...session.parked?.reason ?? session.parkReason ? { parked: session.parked?.reason ?? session.parkReason } : {},
|
|
35621
35754
|
...session.instructionsSha ? { instructionsSha: session.instructionsSha } : {},
|
|
35622
35755
|
...takesImages(session) ? { promptImages: true } : {},
|
|
35623
35756
|
...session.usage ? { usage: session.usage } : {},
|
|
35624
|
-
busy: !!(session.active || session.pending || session.children.size || session.assignments.size || hasActiveMail(session) || reviewing(session))
|
|
35757
|
+
busy: !!(session.active || session.pending || session.parked || session.parkReason || session.children.size || session.assignments.size || hasActiveMail(session) || reviewing(session))
|
|
35625
35758
|
};
|
|
35626
35759
|
}
|
|
35627
35760
|
function terminal(sessionId, session, state, error51) {
|
|
@@ -35639,7 +35772,7 @@ function createAlpRuntime(options2 = {}) {
|
|
|
35639
35772
|
clearTimeout(waiter.timer);
|
|
35640
35773
|
waiter.resolve(null);
|
|
35641
35774
|
}
|
|
35642
|
-
cancelQuestions(sessionId);
|
|
35775
|
+
cancelQuestions(sessionId, true);
|
|
35643
35776
|
if (session.parent) sessions.get(session.parent)?.assignments.get(sessionId)?.ask?.resolve(toolResult(false, { error: "Turn ended" }));
|
|
35644
35777
|
session.mail = session.mail.filter((event) => !(event.deliveredTurn === turnId && state === "completed"));
|
|
35645
35778
|
for (const event of session.mail) {
|
|
@@ -35797,7 +35930,7 @@ function createAlpRuntime(options2 = {}) {
|
|
|
35797
35930
|
if (method === "turn/completed" && params.turn.id === session.active) {
|
|
35798
35931
|
const state = params.turn.status === "completed" ? "completed" : params.turn.status === "interrupted" ? "canceled" : "failed";
|
|
35799
35932
|
if (state === "failed" && LIMIT_ERRORS.includes(params.turn.error?.codexErrorInfo)) {
|
|
35800
|
-
if (session.
|
|
35933
|
+
if (session.role !== "supervisor") session.parkReason = `the ${label(session.runtimeKind)} usage limit was reached`;
|
|
35801
35934
|
void limitReached(session, params.turn.error);
|
|
35802
35935
|
}
|
|
35803
35936
|
terminal(
|
|
@@ -35813,6 +35946,7 @@ function createAlpRuntime(options2 = {}) {
|
|
|
35813
35946
|
if (!session || session.closed) return;
|
|
35814
35947
|
session.closed = true;
|
|
35815
35948
|
cancelQuestions(sessionId);
|
|
35949
|
+
approvalLedger.delete(sessionId);
|
|
35816
35950
|
releaseClaims(sessionId, session.mapping.agent.projectRoot);
|
|
35817
35951
|
await Promise.all(
|
|
35818
35952
|
[...session.children, ...session.supervisor ? [session.supervisor] : []].map(closeSession)
|
|
@@ -36079,7 +36213,7 @@ ${output.slice(0, 4e3)}` : ""}`;
|
|
|
36079
36213
|
const root = sessions.get(rootId);
|
|
36080
36214
|
const watcher = root?.mapping.team?.supervisor ? root.mapping.team.supervisor.agent : "supervisor";
|
|
36081
36215
|
if (!root?.supervisor || entry.from === "supervisor" || entry.agent === watcher) return;
|
|
36082
|
-
const line = entry.event === "assignment.started" ? `${entry.parentAgent} \u2192 ${entry.agent} assignment ${entry.assignmentId}${entry.taskId ? ` for task ${entry.taskId}` : ""} (${entry.model}, ${entry.thinking ?? "default effort"}, ${entry.mode}, ${entry.isolation}${entry.wait ? "" : ", async"}): ${clip(entry.task)}` : entry.event === "assignment.finished" ? `${entry.agent} assignment ${entry.assignmentId} ${entry.status}${entry.handoff ? `, handoff ${entry.handoff.outcome}${entry.handoff.verdict ? `, ${verdictLine(entry.handoff.verdict)}` : ""}: ${clip(entry.handoff.summary)}` : ", no handoff"}${entry.error ? ` (${clip(entry.error, 200)})` : ""}${entry.handoff?.discovered?.length ? `; discovered: ${clip(entry.handoff.discovered.join("; "))}` : ""}` : entry.event === "mail" && entry.kind !== "result" && entry.kind !== "board" ? `mail ${entry.kind} from ${entry.from}${entry.replyTo ? ` (reply to ${entry.replyTo})` : ""}: ${clip(entry.body ?? "")}` : entry.event === "human.question" ? `${entry.agent} asked the user: ${clip(entry.body)}` : entry.event === "human.answer" ? `user ${entry.outcome} ${entry.questionId}${entry.answer ? `: ${clip(entry.answer)}` : ""}` : entry.event === "board.pin" ? `${entry.agent} pinned ${entry.kind}${entry.paths ? ` ${entry.paths.join(", ")}` : ""}: ${clip(entry.body)}` : entry.event === "lesson" ? `${entry.agent} recorded a ${entry.scope} lesson: ${clip(entry.lesson)}` : entry.event === "skill" && Array.isArray(entry.roles) ? `${entry.agent} saved skill ${entry.name} for ${entry.roles.join(", ")} with the user's approval` : entry.event === "task" ? `${entry.agent} ${TASK_PAST[entry.action] ?? entry.action} task ${entry.id} "${clip(entry.title, 120)}"${entry.status ? ` (now ${entry.status})` : ""}${entry.detail ? `: ${clip(entry.detail)}` : ""}` : entry.event === "epic.landed" ? `${entry.agent} closed ${entry.id} "${clip(entry.title, 120)}": ${entry.tasks} tasks, ${entry.reworked} reworked, ${entry.unverified?.length ?? 0} closed unverified` : entry.event === "recall" ? `${entry.agent} recalled ${entry.recalled} assignment ${entry.assignmentId}: ${clip(entry.question)}` : entry.event === "issue" ? `${entry.action === "create" ? `opened issue "${clip(entry.title, 200)}"` : `commented on issue #${entry.issue}`} in ${entry.repo} with the user's approval: ${entry.url}` : entry.event?.startsWith("worktree.") ? `${entry.event.slice("worktree.".length)} worktree of ${entry.assignmentId} (${entry.branch})${entry.status ? `: ${entry.status}` : ""}` : void 0;
|
|
36216
|
+
const line = entry.event === "assignment.started" ? `${entry.parentAgent} \u2192 ${entry.agent} assignment ${entry.assignmentId}${entry.taskId ? ` for task ${entry.taskId}` : ""} (${entry.model}, ${entry.thinking ?? "default effort"}, ${entry.mode}, ${entry.isolation}${entry.wait ? "" : ", async"}): ${clip(entry.task)}` : entry.event === "assignment.finished" ? `${entry.agent} assignment ${entry.assignmentId} ${entry.status}${entry.handoff ? `, handoff ${entry.handoff.outcome}${entry.handoff.verdict ? `, ${verdictLine(entry.handoff.verdict)}` : ""}: ${clip(entry.handoff.summary)}` : ", no handoff"}${entry.error ? ` (${clip(entry.error, 200)})` : ""}${entry.handoff?.discovered?.length ? `; discovered: ${clip(entry.handoff.discovered.join("; "))}` : ""}` : entry.event === "mail" && entry.kind !== "result" && entry.kind !== "board" ? `mail ${entry.kind} from ${entry.from}${entry.replyTo ? ` (reply to ${entry.replyTo})` : ""}: ${clip(entry.body ?? "")}` : entry.event === "human.question" ? `${entry.agent} asked the user: ${clip(entry.body)}` : entry.event === "human.answer" ? `user ${entry.outcome} ${entry.questionId}${entry.answer ? `: ${clip(entry.answer)}` : ""}` : entry.event === "board.pin" ? `${entry.agent} pinned ${entry.kind}${entry.paths ? ` ${entry.paths.join(", ")}` : ""}: ${clip(entry.body)}` : entry.event === "lesson" ? `${entry.agent} recorded a ${entry.scope} lesson: ${clip(entry.lesson)}` : entry.event === "skill" && Array.isArray(entry.roles) ? `${entry.agent} saved skill ${entry.name} for ${entry.roles.join(", ")} with the user's approval` : entry.event === "task" ? `${entry.agent} ${TASK_PAST[entry.action] ?? entry.action} task ${entry.id} "${clip(entry.title, 120)}"${entry.status ? ` (now ${entry.status})` : ""}${entry.detail ? `: ${clip(entry.detail)}` : ""}` : entry.event === "epic.landed" ? `${entry.agent} closed ${entry.id} "${clip(entry.title, 120)}": ${entry.tasks} tasks, ${entry.reworked} reworked, ${entry.unverified?.length ?? 0} closed unverified` : entry.event === "recall" ? `${entry.agent} recalled ${entry.recalled} assignment ${entry.assignmentId}: ${clip(entry.question)}` : entry.event === "skill.approval" && !entry.saved ? `skill approval ${entry.approvalId} for ${entry.name}: not saved (${entry.error ?? entry.outcome})` : entry.event === "issue.approval" ? `issue approval ${entry.approvalId} in ${entry.repo}: ${entry.posted ? entry.url : entry.error ?? entry.outcome}` : entry.event === "issue" ? `${entry.action === "create" ? `opened issue "${clip(entry.title, 200)}"` : `commented on issue #${entry.issue}`} in ${entry.repo} with the user's approval: ${entry.url}` : entry.event?.startsWith("worktree.") ? `${entry.event.slice("worktree.".length)} worktree of ${entry.assignmentId} (${entry.branch})${entry.status ? `: ${entry.status}` : ""}` : void 0;
|
|
36083
36217
|
if (line) root.journal.push(stamp(line));
|
|
36084
36218
|
}
|
|
36085
36219
|
const eventful = (journal) => journal.some((line) => !unstamped(line).startsWith("turn ended:"));
|
|
@@ -36122,13 +36256,13 @@ ${kept.join("\n")}
|
|
|
36122
36256
|
${agent}'s session: ${root.runtimeKind}:${root.mapping.model}, thinking ${root.mapping.thinking ?? "default"}, mode ${root.mapping.mode}. Judge ${agent} by this session and its own instructions; your system prompt describes your session (your model, attribution lines, tools), not ${agent}'s.
|
|
36123
36257
|
|
|
36124
36258
|
Review the process against ALP.md, ${agent}'s AGENT.md and the recorded lessons. Ask ${agent} about each mistake with one alp_send to: "parent", kind note, or send nothing when the process was sound.`;
|
|
36125
|
-
await startPrompt(supervisorId, { clientMessageId: `alp-review-${
|
|
36259
|
+
await startPrompt(supervisorId, { clientMessageId: `alp-review-${randomUUID12()}`, delivery: "auto", content: [{ type: "text", text: text3 }] }, "assignment");
|
|
36126
36260
|
}).catch((error51) => runLog(rootId, { event: "supervisor.failed", error: errorData(error51).message }));
|
|
36127
36261
|
}
|
|
36128
36262
|
async function openSupervisor(rootId) {
|
|
36129
36263
|
const root = sessions.get(rootId);
|
|
36130
36264
|
if (!root || root.closed || root.supervisor || root.parent) return;
|
|
36131
|
-
const id = `alp-supervisor-${
|
|
36265
|
+
const id = `alp-supervisor-${randomUUID12()}`;
|
|
36132
36266
|
childContexts.set(id, { parent: rootId, callId: `supervisor-${rootId}`, graph: {}, workflow: root.mapping.workflow, ancestry: [...root.ancestry, "supervisor"], role: "supervisor" });
|
|
36133
36267
|
const { restore: _restore, ...inherited2 } = root.spec;
|
|
36134
36268
|
const watcher = root.mapping.team?.supervisor || { agent: "supervisor" };
|
|
@@ -36377,8 +36511,8 @@ Review the process against ALP.md, ${agent}'s AGENT.md and the recorded lessons.
|
|
|
36377
36511
|
const body = JSON.stringify(paused, null, 2) + "\n";
|
|
36378
36512
|
pauseWrites = pauseWrites.then(async () => {
|
|
36379
36513
|
await mkdir12(path30.dirname(file3), { recursive: true, mode: 448 });
|
|
36380
|
-
const temporary = `${file3}.${process.pid}.${
|
|
36381
|
-
await
|
|
36514
|
+
const temporary = `${file3}.${process.pid}.${randomUUID12().slice(0, 8)}.tmp`;
|
|
36515
|
+
await writeFile14(temporary, body, { mode: 384 });
|
|
36382
36516
|
await moveFile(temporary, file3);
|
|
36383
36517
|
}).catch(() => {
|
|
36384
36518
|
});
|
|
@@ -36387,7 +36521,7 @@ Review the process against ALP.md, ${agent}'s AGENT.md and the recorded lessons.
|
|
|
36387
36521
|
for (const [id, session] of sessions) {
|
|
36388
36522
|
if (session.parent || session.closed || project !== void 0 && session.mapping.agent.projectRoot !== project) continue;
|
|
36389
36523
|
const text3 = typeof message === "string" ? message : message(wordsFor(session));
|
|
36390
|
-
emit(id, { type: "item", item: { kind: "notice", id: `notice-${
|
|
36524
|
+
emit(id, { type: "item", item: { kind: "notice", id: `notice-${randomUUID12().slice(0, 8)}`, level, text: text3 } });
|
|
36391
36525
|
runLog(id, { event: "notice", level, text: text3 });
|
|
36392
36526
|
}
|
|
36393
36527
|
}
|
|
@@ -36514,20 +36648,34 @@ Review the process against ALP.md, ${agent}'s AGENT.md and the recorded lessons.
|
|
|
36514
36648
|
else post(session.parent, { kind: "stalled", from: assignment.agent, assignment: sessionId, body: `Parked: ${session.parked.reason}. ALP continues this assignment where it stopped when ${label(session.runtimeKind)} is resumed (${options2.autoResume !== false && (pauseOf(session.runtimeKind)?.by ?? "alpd") === "alpd" ? `ALP resumes it as soon as ${label(session.runtimeKind)} says the limit lifted` : "the user runs alp resume"}). Wait for it, start other work, or stop it with alp_cancel and give the rest to an agent on another runtime (pass model claude:\u2026 or codex:\u2026); what it changed stays in your checkout or its worktree.` });
|
|
36515
36649
|
}
|
|
36516
36650
|
function continueParked(sessionId, session) {
|
|
36517
|
-
|
|
36518
|
-
|
|
36519
|
-
|
|
36520
|
-
|
|
36521
|
-
|
|
36522
|
-
|
|
36523
|
-
|
|
36524
|
-
|
|
36525
|
-
|
|
36526
|
-
|
|
36527
|
-
|
|
36528
|
-
|
|
36529
|
-
|
|
36530
|
-
|
|
36651
|
+
void enqueue(async () => {
|
|
36652
|
+
if (session.closed || session.active || session.pending || !session.parked || pauseOf(session.runtimeKind)) return;
|
|
36653
|
+
const parked = session.parked;
|
|
36654
|
+
const { reason, prompt } = parked;
|
|
36655
|
+
session.parked = void 0;
|
|
36656
|
+
session.wakeHeld = false;
|
|
36657
|
+
session.lastActivity = Date.now();
|
|
36658
|
+
const parent = session.parent ? sessions.get(session.parent) : void 0;
|
|
36659
|
+
const assignment = parent?.assignments.get(sessionId);
|
|
36660
|
+
runLog(rootOf2(sessionId), { event: "assignment.resumed", assignmentId: sessionId, agent: session.mapping.agent.name });
|
|
36661
|
+
if (assignment) emit(session.parent, { type: "assignment", assignment: assignmentSnapshot(assignment, "running") });
|
|
36662
|
+
try {
|
|
36663
|
+
await startPrompt(sessionId, {
|
|
36664
|
+
clientMessageId: `alp-resume-${randomUUID12()}`,
|
|
36665
|
+
delivery: "auto",
|
|
36666
|
+
content: [{ type: "text", text: prompt ?? (session.parent ? `ALP resumed this assignment; it had stopped because ${reason}. Continue where you left off: your earlier work in this session and its files are intact. Finish with alp_handoff as before.` : `ALP resumed this session; it had stopped because ${reason}. Continue the user's request where you left off: your earlier work in this thread and its files are intact.`) }]
|
|
36667
|
+
}, session.parent ? "assignment" : "wake");
|
|
36668
|
+
session.resumedTurn = session.active;
|
|
36669
|
+
emit(sessionId, { type: "session.updated", session: snapshot(sessionId, session) });
|
|
36670
|
+
} catch (error51) {
|
|
36671
|
+
if (!session.closed && !session.active) {
|
|
36672
|
+
session.parked = parked;
|
|
36673
|
+
emit(sessionId, { type: "session.updated", session: snapshot(sessionId, session) });
|
|
36674
|
+
if (assignment) emit(session.parent, { type: "assignment", assignment: assignmentSnapshot(assignment, "parked") });
|
|
36675
|
+
}
|
|
36676
|
+
notice("warning", `Could not resume ${session.mapping.agent.name}: ${errorData(error51).message}`);
|
|
36677
|
+
}
|
|
36678
|
+
});
|
|
36531
36679
|
}
|
|
36532
36680
|
async function limitReset(session, error51) {
|
|
36533
36681
|
const at2 = (seconds) => typeof seconds === "number" && seconds > 0 ? new Date(seconds * 1e3).toISOString() : void 0;
|
|
@@ -36544,8 +36692,9 @@ Review the process against ALP.md, ${agent}'s AGENT.md and the recorded lessons.
|
|
|
36544
36692
|
}
|
|
36545
36693
|
async function limitReached(session, error51) {
|
|
36546
36694
|
const kind = session.runtimeKind;
|
|
36547
|
-
if (
|
|
36695
|
+
if (pauseOf(kind)) return;
|
|
36548
36696
|
const resetsAt = await limitReset(session, error51);
|
|
36697
|
+
if (closed || session.closed || pauseOf(kind)) return;
|
|
36549
36698
|
const pause = { since: (/* @__PURE__ */ new Date()).toISOString(), by: "alpd", reason: `${label(kind)} usage limit reached`, ...resetsAt ? { resetsAt } : {} };
|
|
36550
36699
|
pauseRuntime(kind, pause);
|
|
36551
36700
|
const other = RUNTIMES.find((candidate) => candidate !== kind && !pauseOf(candidate));
|
|
@@ -36681,15 +36830,15 @@ ${cut(session.brief, BRIEF_CHARS)}`);
|
|
|
36681
36830
|
const shown = JSON.stringify(items);
|
|
36682
36831
|
if (!items.length || shown === session.tasksShown || session.closed) return;
|
|
36683
36832
|
session.tasksShown = shown;
|
|
36684
|
-
emit(sessionId, { type: "item", item: { kind: "todo", id: `tasks-${
|
|
36833
|
+
emit(sessionId, { type: "item", item: { kind: "todo", id: `tasks-${randomUUID12().slice(0, 8)}`, items } });
|
|
36685
36834
|
}
|
|
36686
36835
|
function lessonFiles(mapping) {
|
|
36687
36836
|
return [path30.join(mapping.agent.projectRoot, ".alp", LESSONS_FILE), ...options2.libraryDir ? [path30.join(options2.libraryDir, LESSONS_FILE)] : []];
|
|
36688
36837
|
}
|
|
36689
|
-
async function confirm(sessionId, session, body) {
|
|
36838
|
+
async function confirm(sessionId, session, body, approval) {
|
|
36690
36839
|
if ([...userQuestions.values()].some((pending) => pending.question.sessionId === sessionId)) return { approved: false, outcome: "busy", feedback: "A question is already waiting for the user" };
|
|
36691
36840
|
const say = wordsFor(session);
|
|
36692
|
-
const result = await askUser(sessionId, session, void 0, body, [say.approve, say.reject]);
|
|
36841
|
+
const result = await askUser(sessionId, session, void 0, body, [say.approve, say.reject], approval);
|
|
36693
36842
|
let value;
|
|
36694
36843
|
try {
|
|
36695
36844
|
value = JSON.parse(result.contentItems[0].text);
|
|
@@ -36700,25 +36849,45 @@ ${cut(session.brief, BRIEF_CHARS)}`);
|
|
|
36700
36849
|
const answer = String(value.answer).trim();
|
|
36701
36850
|
return APPROVALS.includes(answer.toLowerCase()) ? { approved: true } : { approved: false, outcome: "answered", feedback: answer };
|
|
36702
36851
|
}
|
|
36703
|
-
const declined = (decision) => toolResult(true, {
|
|
36704
|
-
approved: false,
|
|
36705
|
-
outcome: decision.outcome,
|
|
36706
|
-
...decision.feedback ? { feedback: decision.feedback } : {},
|
|
36707
|
-
next: "Nothing was saved or posted. Revise from the feedback and propose again, or drop it."
|
|
36708
|
-
});
|
|
36709
36852
|
const text2 = (value, limit) => typeof value === "string" && !!value.trim() && value.length <= limit;
|
|
36853
|
+
const approvalCancellation = /* @__PURE__ */ new WeakMap();
|
|
36854
|
+
const approvalLedger = /* @__PURE__ */ new Map();
|
|
36855
|
+
const approvalsOf = (sessionId) => {
|
|
36856
|
+
let approvals = approvalLedger.get(sessionId);
|
|
36857
|
+
if (!approvals) approvalLedger.set(sessionId, approvals = /* @__PURE__ */ new Map());
|
|
36858
|
+
return approvals;
|
|
36859
|
+
};
|
|
36860
|
+
const approvalCanceled = (session, cancellation) => closed || session.closed || cancellation !== (approvalCancellation.get(session) ?? 0);
|
|
36710
36861
|
async function skillTool(sessionId, session, args) {
|
|
36711
|
-
|
|
36862
|
+
const cancellation = approvalCancellation.get(session) ?? 0;
|
|
36863
|
+
args = structuredClone(args);
|
|
36864
|
+
if (!plainObject(args, ["name", "description", "body", "roles", "lessons", "replace", "requestId"]) || typeof args.name !== "string" || !/^[a-z0-9][a-z0-9-]{1,62}$/.test(args.name) || !text2(args.description, 300) || !text2(args.body, SKILL_BODY_CHARS) || !Array.isArray(args.roles) || !args.roles.length || args.roles.length > 10 || !args.roles.every((role) => typeof role === "string" && /^[\w.-]+$/.test(role) && role !== "." && role !== "..") || args.lessons !== void 0 && (!Array.isArray(args.lessons) || args.lessons.length > 50 || !args.lessons.every((lesson) => text2(lesson, LESSON_CHARS2))) || args.replace !== void 0 && typeof args.replace !== "boolean") {
|
|
36712
36865
|
return toolResult(false, { error: `A skill needs a name (lowercase letters, digits, hyphens), a one-line description, a body of at most ${SKILL_BODY_CHARS} characters, and the roles that get it` });
|
|
36713
36866
|
}
|
|
36714
|
-
if (!
|
|
36715
|
-
const
|
|
36867
|
+
if (args.requestId !== void 0 && !text2(args.requestId, 100)) return toolResult(false, { error: "requestId must be a nonempty string of at most 100 characters" });
|
|
36868
|
+
const library = options2.libraryDir;
|
|
36869
|
+
if (!library) return toolResult(false, { error: "This host keeps no user skill library" });
|
|
36870
|
+
const known = /* @__PURE__ */ new Set([...STARTER_ROLES, ...await discoverAgents(session.mapping.agent.projectRoot, { library, templates: options2.templates })]);
|
|
36716
36871
|
const unknown2 = args.roles.filter((role) => !known.has(role));
|
|
36717
36872
|
if (unknown2.length) return toolResult(false, { error: `Unknown roles: ${unknown2.join(", ")}. Roles are ${[...known].join(", ")}` });
|
|
36718
|
-
const file3 = path30.join(
|
|
36873
|
+
const file3 = path30.join(library, "skills", args.name, "SKILL.md");
|
|
36874
|
+
const content = `---
|
|
36875
|
+
name: ${args.name}
|
|
36876
|
+
description: ${JSON.stringify(args.description.replace(/\s+/g, " ").trim())}
|
|
36877
|
+
---
|
|
36878
|
+
|
|
36879
|
+
${args.body.trim()}
|
|
36880
|
+
`;
|
|
36881
|
+
const lessons2 = (args.lessons ?? []).map((lesson) => lesson.replace(/\s+/g, " ").trim());
|
|
36882
|
+
const draftKey = JSON.stringify(["skill", content, args.roles, lessons2, !!args.replace]);
|
|
36883
|
+
const key2 = `skill:${args.requestId ?? draftKey}`;
|
|
36884
|
+
const approvals = approvalsOf(sessionId);
|
|
36885
|
+
const previous = approvals.get(key2);
|
|
36886
|
+
if (previous && previous.draftKey !== draftKey) return toolResult(false, { error: "requestId was already used for a different draft" });
|
|
36887
|
+
if (previous) return toolResult(true, previous.result ?? { status: "pending_approval", approvalId: previous.approvalId, file: file3 });
|
|
36719
36888
|
const existing = await readFile18(file3, "utf8").catch(() => void 0);
|
|
36720
36889
|
if (existing !== void 0 && !args.replace) return toolResult(false, { error: `The library already has a skill named ${args.name} (${file3}); read it, then pass replace: true to propose a new version, or choose another name` });
|
|
36721
|
-
const rolesFile = path30.join(
|
|
36890
|
+
const rolesFile = path30.join(library, "role-skills.json");
|
|
36722
36891
|
const readRoles = async () => {
|
|
36723
36892
|
const raw = await readFile18(rolesFile, "utf8").catch(() => "{}");
|
|
36724
36893
|
const roles = JSON.parse(raw);
|
|
@@ -36730,62 +36899,77 @@ ${cut(session.brief, BRIEF_CHARS)}`);
|
|
|
36730
36899
|
} catch (error51) {
|
|
36731
36900
|
return toolResult(false, { error: errorData(error51).message });
|
|
36732
36901
|
}
|
|
36733
|
-
|
|
36734
|
-
|
|
36735
|
-
|
|
36736
|
-
|
|
36737
|
-
|
|
36738
|
-
${args.body.trim()}
|
|
36739
|
-
`;
|
|
36740
|
-
const lessons2 = (args.lessons ?? []).map((lesson) => lesson.replace(/\s+/g, " ").trim());
|
|
36902
|
+
if (approvalCanceled(session, cancellation)) return toolResult(false, { error: "Skill proposal canceled or session closed; no approval was created" });
|
|
36903
|
+
if ([...userQuestions.values()].some((pending) => pending.question.sessionId === sessionId)) return toolResult(false, { error: "A question is already waiting for the user; finish it before proposing a skill" });
|
|
36904
|
+
const approvalId = `q-${randomUUID12()}`;
|
|
36905
|
+
const record4 = { approvalId, draftKey };
|
|
36906
|
+
approvals.set(key2, record4);
|
|
36741
36907
|
const say = wordsFor(session);
|
|
36742
|
-
const decision =
|
|
36908
|
+
const decision = confirm(
|
|
36743
36909
|
sessionId,
|
|
36744
36910
|
session,
|
|
36745
36911
|
say.skill(session.mapping.agent.name, existing !== void 0, file3, args.roles.join(", "), lessons2) + `
|
|
36746
36912
|
${say.answerLine(session.mapping.agent.name)}
|
|
36747
36913
|
|
|
36748
|
-
${content}
|
|
36914
|
+
${content}`,
|
|
36915
|
+
{ id: approvalId }
|
|
36749
36916
|
);
|
|
36750
|
-
|
|
36751
|
-
|
|
36752
|
-
|
|
36753
|
-
|
|
36754
|
-
|
|
36755
|
-
|
|
36756
|
-
|
|
36757
|
-
|
|
36758
|
-
|
|
36759
|
-
|
|
36760
|
-
|
|
36761
|
-
|
|
36762
|
-
|
|
36763
|
-
|
|
36764
|
-
|
|
36765
|
-
|
|
36766
|
-
|
|
36767
|
-
|
|
36768
|
-
const
|
|
36769
|
-
const
|
|
36770
|
-
if (
|
|
36771
|
-
|
|
36772
|
-
|
|
36773
|
-
|
|
36774
|
-
|
|
36775
|
-
|
|
36776
|
-
|
|
36777
|
-
|
|
36778
|
-
|
|
36779
|
-
|
|
36780
|
-
|
|
36781
|
-
|
|
36782
|
-
|
|
36783
|
-
|
|
36784
|
-
|
|
36785
|
-
|
|
36917
|
+
const save2 = async () => {
|
|
36918
|
+
if (existing === void 0 && !args.replace && await readFile18(file3, "utf8").then(() => true, () => false)) {
|
|
36919
|
+
return { approved: true, saved: false, error: `A skill named ${args.name} was saved meanwhile (${file3}); nothing was overwritten`, next: "Read it, then propose again with replace: true if yours should replace it." };
|
|
36920
|
+
}
|
|
36921
|
+
try {
|
|
36922
|
+
await mkdir12(path30.dirname(file3), { recursive: true });
|
|
36923
|
+
await writeFile14(file3, content);
|
|
36924
|
+
const roles = await readRoles();
|
|
36925
|
+
for (const role of args.roles) {
|
|
36926
|
+
const list2 = Array.isArray(roles[role]) ? roles[role] : [];
|
|
36927
|
+
if (!list2.includes(args.name)) list2.push(args.name);
|
|
36928
|
+
roles[role] = list2;
|
|
36929
|
+
}
|
|
36930
|
+
await writeFile14(rolesFile, JSON.stringify(roles, null, 2) + "\n");
|
|
36931
|
+
} catch (error51) {
|
|
36932
|
+
return { approved: true, saved: false, error: errorData(error51).message, next: "Tell the user; nothing is retried automatically." };
|
|
36933
|
+
}
|
|
36934
|
+
const removed = /* @__PURE__ */ new Set();
|
|
36935
|
+
for (const lessonFile of lessonFiles(session.mapping)) {
|
|
36936
|
+
const current = await readFile18(lessonFile, "utf8").catch(() => void 0);
|
|
36937
|
+
if (current === void 0) continue;
|
|
36938
|
+
const kept = current.split("\n").filter((line) => {
|
|
36939
|
+
const match = /^- \d{4}-\d\d-\d\d: (.*)$/.exec(line);
|
|
36940
|
+
if (!match || !lessons2.includes(match[1].trim())) return true;
|
|
36941
|
+
removed.add(match[1].trim());
|
|
36942
|
+
return false;
|
|
36943
|
+
});
|
|
36944
|
+
if (kept.length !== current.split("\n").length) await writeFile14(lessonFile, kept.join("\n")).catch(() => {
|
|
36945
|
+
});
|
|
36946
|
+
}
|
|
36947
|
+
runLog(rootOf2(sessionId), { event: "skill", agent: session.mapping.agent.name, name: args.name, roles: args.roles, replaced: existing !== void 0, lessons: [...removed] });
|
|
36948
|
+
return {
|
|
36949
|
+
approved: true,
|
|
36950
|
+
saved: file3,
|
|
36951
|
+
roles: args.roles,
|
|
36952
|
+
lessonsRemoved: [...removed],
|
|
36953
|
+
...lessons2.length > removed.size ? { lessonsNotFound: lessons2.filter((lesson) => !removed.has(lesson)) } : {},
|
|
36954
|
+
next: "New sessions of those roles list the skill; read the file now if you need it in this one."
|
|
36955
|
+
};
|
|
36956
|
+
};
|
|
36957
|
+
void (async () => {
|
|
36958
|
+
const answer = await decision;
|
|
36959
|
+
const canceled = approvalCanceled(session, cancellation);
|
|
36960
|
+
const result = !answer.approved || canceled ? { approved: false, saved: false, outcome: canceled ? "canceled" : answer.outcome, ...answer.feedback ? { feedback: answer.feedback } : {}, next: "Nothing was saved. Revise from the feedback and propose again with a fresh requestId, or drop it." } : await save2();
|
|
36961
|
+
record4.result = { status: "resolved", approvalId, name: args.name, ...result };
|
|
36962
|
+
runLog(rootOf2(sessionId), { event: "skill.approval", agent: session.mapping.agent.name, ...record4.result });
|
|
36963
|
+
if (!closed && !session.closed && sessions.get(sessionId) === session) {
|
|
36964
|
+
post(sessionId, { kind: "result", from: "ALP", assignment: approvalId, body: `Skill approval result: ${JSON.stringify(record4.result)}` });
|
|
36965
|
+
}
|
|
36966
|
+
})();
|
|
36967
|
+
return toolResult(true, { status: "pending_approval", approvalId, file: file3, next: "Continue other work or end your turn. ALP sends the user's decision, and whether the skill was saved, by mail; do not resubmit this draft." });
|
|
36786
36968
|
}
|
|
36787
36969
|
async function issueTool(sessionId, session, args) {
|
|
36788
|
-
|
|
36970
|
+
const cancellation = approvalCancellation.get(session) ?? 0;
|
|
36971
|
+
args = structuredClone(args);
|
|
36972
|
+
if (!plainObject(args, ["action", "target", "query", "title", "body", "issue", "labels", "requestId"]) || !["search", "create", "comment"].includes(args.action) || !["project", "alp"].includes(args.target)) {
|
|
36789
36973
|
return toolResult(false, { error: "An issue call needs action (search, create or comment) and target (project or alp)" });
|
|
36790
36974
|
}
|
|
36791
36975
|
const project = session.mapping.agent.projectRoot;
|
|
@@ -36811,6 +36995,7 @@ ${content}`
|
|
|
36811
36995
|
if (args.labels !== void 0 && (args.action !== "create" || !Array.isArray(args.labels) || args.labels.length > 10 || !args.labels.every((label2) => text2(label2, 50)))) {
|
|
36812
36996
|
return toolResult(false, { error: "labels are up to 10 existing label names, for create" });
|
|
36813
36997
|
}
|
|
36998
|
+
if (args.requestId !== void 0 && !text2(args.requestId, 100)) return toolResult(false, { error: "requestId must be a nonempty string of at most 100 characters" });
|
|
36814
36999
|
if (args.action === "create" && args.labels?.length) {
|
|
36815
37000
|
try {
|
|
36816
37001
|
const labels = JSON.parse(await github(["label", "list", "-R", repo, "--limit", "1000", "--json", "name"], { cwd: project }));
|
|
@@ -36821,9 +37006,20 @@ ${content}`
|
|
|
36821
37006
|
return toolResult(false, { repo, error: `Could not validate issue labels: ${errorData(error51).message}` });
|
|
36822
37007
|
}
|
|
36823
37008
|
}
|
|
37009
|
+
if (approvalCanceled(session, cancellation)) return toolResult(false, { error: "Issue proposal canceled or session closed; no approval was created" });
|
|
37010
|
+
const draftKey = JSON.stringify([repo, args.action, args.title ?? null, args.issue ?? null, args.body, [...args.labels ?? []].sort()]);
|
|
37011
|
+
const key2 = `issue:${args.requestId ?? draftKey}`;
|
|
37012
|
+
const approvals = approvalsOf(sessionId);
|
|
37013
|
+
const previous = approvals.get(key2);
|
|
37014
|
+
if (previous && previous.draftKey !== draftKey) return toolResult(false, { error: "requestId was already used for a different draft" });
|
|
37015
|
+
if (previous) return toolResult(true, previous.result ?? { status: "pending_approval", approvalId: previous.approvalId, repo });
|
|
37016
|
+
if ([...userQuestions.values()].some((pending) => pending.question.sessionId === sessionId)) return toolResult(false, { error: "A question is already waiting for the user; finish it before proposing another issue draft" });
|
|
37017
|
+
const approvalId = `q-${randomUUID12()}`;
|
|
37018
|
+
const record4 = { approvalId, draftKey };
|
|
37019
|
+
approvals.set(key2, record4);
|
|
36824
37020
|
const where = `${repo}${args.target === "alp" ? " (ALP itself)" : ""}`;
|
|
36825
37021
|
const say = wordsFor(session);
|
|
36826
|
-
const decision =
|
|
37022
|
+
const decision = confirm(
|
|
36827
37023
|
sessionId,
|
|
36828
37024
|
session,
|
|
36829
37025
|
say.issue(session.mapping.agent.name, args.action === "create", args.issue, where) + `
|
|
@@ -36832,18 +37028,31 @@ ${say.answerLine(session.mapping.agent.name)}
|
|
|
36832
37028
|
` + (args.action === "create" ? `${say.issueTitle}: ${args.title}
|
|
36833
37029
|
${args.labels?.length ? `${say.issueLabels}: ${args.labels.join(", ")}
|
|
36834
37030
|
` : ""}
|
|
36835
|
-
` : "") + args.body
|
|
37031
|
+
` : "") + args.body.trim() + ISSUE_FOOTER,
|
|
37032
|
+
{ id: approvalId }
|
|
36836
37033
|
);
|
|
36837
|
-
|
|
36838
|
-
|
|
36839
|
-
|
|
36840
|
-
const
|
|
36841
|
-
|
|
36842
|
-
|
|
36843
|
-
|
|
36844
|
-
|
|
36845
|
-
|
|
36846
|
-
|
|
37034
|
+
void (async () => {
|
|
37035
|
+
const answer = await decision;
|
|
37036
|
+
let result;
|
|
37037
|
+
const canceled = approvalCanceled(session, cancellation);
|
|
37038
|
+
if (!answer.approved || canceled) {
|
|
37039
|
+
result = { approved: false, posted: false, outcome: canceled ? "canceled" : answer.outcome, ...answer.feedback ? { feedback: answer.feedback } : {} };
|
|
37040
|
+
} else {
|
|
37041
|
+
try {
|
|
37042
|
+
const body = args.body.trim() + ISSUE_FOOTER;
|
|
37043
|
+
const output = args.action === "create" ? await github(["issue", "create", "-R", repo, "--title", args.title, "--body-file", "-", ...(args.labels ?? []).flatMap((label2) => ["--label", label2])], { cwd: project, input: body }) : await github(["issue", "comment", String(args.issue), "-R", repo, "--body-file", "-"], { cwd: project, input: body });
|
|
37044
|
+
result = { approved: true, posted: true, url: output.trim().split("\n").at(-1) ?? "" };
|
|
37045
|
+
} catch (error51) {
|
|
37046
|
+
result = { approved: true, posted: false, error: errorData(error51).message, next: "Tell the user; do not retry this draft automatically: GitHub may have accepted it before reporting an error." };
|
|
37047
|
+
}
|
|
37048
|
+
}
|
|
37049
|
+
record4.result = { status: "resolved", approvalId, repo, ...result };
|
|
37050
|
+
runLog(rootOf2(sessionId), { event: "issue.approval", action: args.action, ...args.title ? { title: args.title } : {}, ...args.issue ? { issue: args.issue } : {}, ...record4.result });
|
|
37051
|
+
if (!closed && !session.closed && sessions.get(sessionId) === session) {
|
|
37052
|
+
post(sessionId, { kind: "result", from: "ALP", assignment: approvalId, body: `Issue approval result: ${JSON.stringify(record4.result)}` });
|
|
37053
|
+
}
|
|
37054
|
+
})();
|
|
37055
|
+
return toolResult(true, { status: "pending_approval", approvalId, repo, next: "Continue other work. ALP sends the decision and posting result by mail; do not resubmit this draft." });
|
|
36847
37056
|
}
|
|
36848
37057
|
function touch(sessionId, session) {
|
|
36849
37058
|
const now2 = Date.now();
|
|
@@ -36867,6 +37076,8 @@ ${args.labels?.length ? `${say.issueLabels}: ${args.labels.join(", ")}
|
|
|
36867
37076
|
if (session && !session.closed) {
|
|
36868
37077
|
emit(sessionId, { type: "mail", mail: publicEvent(mail) });
|
|
36869
37078
|
session.mail.push(mail);
|
|
37079
|
+
if (mail.kind === "result" || mail.kind === "question") session.wakes = 0;
|
|
37080
|
+
if (mail.kind === "result" || mail.kind === "question" || mail.kind === "checkin") session.wakeFailures = 0;
|
|
36870
37081
|
deliver(sessionId, session);
|
|
36871
37082
|
}
|
|
36872
37083
|
return mail;
|
|
@@ -36889,7 +37100,7 @@ ${args.labels?.length ? `${say.issueLabels}: ${args.labels.join(", ")}
|
|
|
36889
37100
|
if (session.mail.some((event) => !event.deliveredTurn && !event.passive && !event.defer)) void steerMail(sessionId, session);
|
|
36890
37101
|
} else if (!waking) {
|
|
36891
37102
|
return;
|
|
36892
|
-
} else if (!session.wakeBlocked && session.wakes < MAX_WAKES) {
|
|
37103
|
+
} else if (!session.wakeBlocked && session.wakeFailures < MAX_WAKES && (session.wakes < MAX_WAKES || session.mail.some((event) => !event.deliveredTurn && event.kind === "checkin"))) {
|
|
36893
37104
|
autoWake(sessionId, session);
|
|
36894
37105
|
} else if (session.parent && !session.wakeBlocked && !held(session)) {
|
|
36895
37106
|
session.settle?.("failed", `Wake limit (${MAX_WAKES}) reached with mail outstanding`);
|
|
@@ -36900,7 +37111,7 @@ ${args.labels?.length ? `${say.issueLabels}: ${args.labels.join(", ")}
|
|
|
36900
37111
|
const batch2 = takeBatch(session.mail, (event) => !event.defer);
|
|
36901
37112
|
if (!batch2.length) return;
|
|
36902
37113
|
for (const event of batch2) event.deliveredTurn = STEERING;
|
|
36903
|
-
const id = `alp-mail-${
|
|
37114
|
+
const id = `alp-mail-${randomUUID12()}`;
|
|
36904
37115
|
const text3 = promptSafe(renderMail(batch2, session.parentAgent));
|
|
36905
37116
|
let steered = false;
|
|
36906
37117
|
try {
|
|
@@ -36923,6 +37134,7 @@ ${args.labels?.length ? `${say.issueLabels}: ${args.labels.join(", ")}
|
|
|
36923
37134
|
session.wakeHeld = true;
|
|
36924
37135
|
return;
|
|
36925
37136
|
}
|
|
37137
|
+
const checkIn2 = session.mail.find((event) => !event.deliveredTurn && event.kind === "checkin");
|
|
36926
37138
|
const batch2 = takeBatch(session.mail, () => true);
|
|
36927
37139
|
if (!batch2.length) return;
|
|
36928
37140
|
for (const event of batch2) event.deliveredTurn = STARTING;
|
|
@@ -36935,11 +37147,13 @@ ${args.labels?.length ? `${say.issueLabels}: ${args.labels.join(", ")}
|
|
|
36935
37147
|
release();
|
|
36936
37148
|
return deliver(sessionId, session);
|
|
36937
37149
|
}
|
|
37150
|
+
if (session.wakeFailures >= MAX_WAKES || session.wakes >= MAX_WAKES && (!checkIn2 || !session.mail.includes(checkIn2))) return release();
|
|
36938
37151
|
if (!batch2.every((event) => event.kind === "checkin")) session.wakes++;
|
|
36939
|
-
const id = `alp-wake-${
|
|
37152
|
+
const id = `alp-wake-${randomUUID12()}`;
|
|
36940
37153
|
try {
|
|
36941
37154
|
await startPrompt(sessionId, { clientMessageId: id, delivery: "auto", content: [{ type: "text", text: renderMail(batch2, session.parentAgent) }] }, "wake", batch2);
|
|
36942
37155
|
} catch {
|
|
37156
|
+
session.wakeFailures++;
|
|
36943
37157
|
release();
|
|
36944
37158
|
deliver(sessionId, session);
|
|
36945
37159
|
}
|
|
@@ -37039,6 +37253,17 @@ ${args.labels?.length ? `${say.issueLabels}: ${args.labels.join(", ")}
|
|
|
37039
37253
|
waiter.resolve("user");
|
|
37040
37254
|
}
|
|
37041
37255
|
}
|
|
37256
|
+
function releaseQuestions(sessionId) {
|
|
37257
|
+
for (const { question, settle, asked } of [...userQuestions.values()]) {
|
|
37258
|
+
if (question.sessionId !== sessionId || !asked) continue;
|
|
37259
|
+
settle("canceled", void 0, "the user wrote instead of answering", toolResult(true, {
|
|
37260
|
+
status: "user_wrote",
|
|
37261
|
+
userMessage: true,
|
|
37262
|
+
question: question.id,
|
|
37263
|
+
next: `${USER_WROTE} Their message may be the answer to your question; if it is not, ask again later.`
|
|
37264
|
+
}));
|
|
37265
|
+
}
|
|
37266
|
+
}
|
|
37042
37267
|
const assignmentSnapshot = (assignment, status) => ({
|
|
37043
37268
|
id: assignment.id,
|
|
37044
37269
|
agent: assignment.agent,
|
|
@@ -37248,7 +37473,7 @@ ${refused}`, next: "Fix what it reports, then call alp_handoff again." });
|
|
|
37248
37473
|
const pending = [...session.assignments.keys()].some((id) => !ids || ids.has(id)) || takeBatch(session.mail, (event) => event.kind !== "board" && !event.defer && accept(event)).length > 0;
|
|
37249
37474
|
const events = pending ? await waitFor(sessionId, session, accept, Math.min(args.timeoutMs ?? 3e5, 9e5)) : [];
|
|
37250
37475
|
if (events === "user") return toolResult(true, { events: [], userMessage: true, running: running2(session), next: USER_WROTE });
|
|
37251
|
-
if (!events) return toolResult(false, { error: "Turn ended" });
|
|
37476
|
+
if (!events) return toolResult(false, session.closed || session.wakeBlocked ? { error: "Turn ended" } : { error: TURN_ENDED, running: running2(session) });
|
|
37252
37477
|
return toolResult(true, { events: events.map(publicEvent), running: running2(session) });
|
|
37253
37478
|
}
|
|
37254
37479
|
function sendTool(sessionId, session, args) {
|
|
@@ -37278,10 +37503,10 @@ ${refused}`, next: "Fix what it reports, then call alp_handoff again." });
|
|
|
37278
37503
|
}
|
|
37279
37504
|
return toolResult(true, { sent: post(assignment.id, { kind: args.kind, from, assignment: assignment.id, body: args.body, ...args.replyTo ? { replyTo: args.replyTo } : {} }).id });
|
|
37280
37505
|
}
|
|
37281
|
-
function askUser(sessionId, session, assignment, body, options3) {
|
|
37506
|
+
function askUser(sessionId, session, assignment, body, options3, approval, asked = false) {
|
|
37282
37507
|
const rootId = rootOf2(sessionId);
|
|
37283
37508
|
const question = {
|
|
37284
|
-
id: `q-${
|
|
37509
|
+
id: approval?.id ?? `q-${randomUUID12().slice(0, 8)}`,
|
|
37285
37510
|
sessionId,
|
|
37286
37511
|
rootId,
|
|
37287
37512
|
agent: session.mapping.agent.name,
|
|
@@ -37307,7 +37532,7 @@ The user answered: "${answer}"`);
|
|
|
37307
37532
|
next: "Decide, and record the assumption in your handoff or final message; or report that you are blocked."
|
|
37308
37533
|
})));
|
|
37309
37534
|
};
|
|
37310
|
-
userQuestions.set(question.id, { question, settle });
|
|
37535
|
+
userQuestions.set(question.id, { question, settle, persistent: !!approval, asked });
|
|
37311
37536
|
if (assignment) assignment.ask = { id: question.id, resolve: (result) => settle("canceled", void 0, void 0, result) };
|
|
37312
37537
|
emit(sessionId, { type: "question", question });
|
|
37313
37538
|
runLog(rootId, { event: "human.question", questionId: question.id, sessionId, agent: question.agent, body, ...options3?.length ? { options: options3 } : {} });
|
|
@@ -37320,9 +37545,9 @@ The user answered: "${answer}"`);
|
|
|
37320
37545
|
if (!session.parent || !sessions.has(session.parent)) return;
|
|
37321
37546
|
post(session.parent, { kind: "note", from: session.mapping.agent.name, assignment: sessionId, body: `[ALP, on behalf of ${session.mapping.agent.name}] ${body}` });
|
|
37322
37547
|
}
|
|
37323
|
-
function cancelQuestions(sessionId) {
|
|
37324
|
-
for (const { question, settle } of [...userQuestions.values()]) {
|
|
37325
|
-
if (question.sessionId === sessionId) settle("canceled", void 0, void 0, toolResult(false, { error: "Turn ended" }));
|
|
37548
|
+
function cancelQuestions(sessionId, turnOnly = false) {
|
|
37549
|
+
for (const { question, settle, persistent } of [...userQuestions.values()]) {
|
|
37550
|
+
if (question.sessionId === sessionId && (!turnOnly || !persistent)) settle("canceled", void 0, void 0, toolResult(false, { error: "Turn ended" }));
|
|
37326
37551
|
}
|
|
37327
37552
|
}
|
|
37328
37553
|
function askTool(sessionId, session, args) {
|
|
@@ -37342,7 +37567,7 @@ The user answered: "${answer}"`);
|
|
|
37342
37567
|
if (to === "user" && session.parent && !session.userOpened) {
|
|
37343
37568
|
return Promise.resolve(toolResult(false, { error: `Only main talks to the user; ask ${session.parentAgent ?? "your requester"} with alp_ask instead` }));
|
|
37344
37569
|
}
|
|
37345
|
-
if (to === "user") return askUser(sessionId, session, assignment, args.question, args.options);
|
|
37570
|
+
if (to === "user") return askUser(sessionId, session, assignment, args.question, args.options, void 0, true);
|
|
37346
37571
|
if (!session.parent || !parent || !assignment) return Promise.resolve(toolResult(false, { error: 'This session has no requester; ask the user with to: "user"' }));
|
|
37347
37572
|
const parentId = session.parent;
|
|
37348
37573
|
return new Promise((resolve2) => {
|
|
@@ -37489,9 +37714,11 @@ The user answered: "${answer}"`);
|
|
|
37489
37714
|
}
|
|
37490
37715
|
const rootId = rootOf2(sessionId);
|
|
37491
37716
|
const root = sessions.get(rootId);
|
|
37492
|
-
|
|
37717
|
+
const advisor = ["advisor", "reviewer"].includes(roleOf(session.mapping, args.agent) ?? "") && childMode === "read-only";
|
|
37718
|
+
const budget = advisor ? "advisorCalls" : "calls";
|
|
37719
|
+
if (root[budget] >= 16) {
|
|
37493
37720
|
return toolResult(false, {
|
|
37494
|
-
error: "
|
|
37721
|
+
error: `${advisor ? "Advisor" : "Implementation"} delegation budget exhausted (16 starts across the root session tree since its last new user turn). Mail wakes and child turns do not reset it; a new accepted user prompt in root (including steering) resets both budgets.`
|
|
37495
37722
|
});
|
|
37496
37723
|
}
|
|
37497
37724
|
const peer = roleOf(session.mapping, args.agent) === "peer";
|
|
@@ -37504,9 +37731,9 @@ The user answered: "${answer}"`);
|
|
|
37504
37731
|
const continued = args.continueFrom !== void 0 ? session.worktrees.get(args.continueFrom) : void 0;
|
|
37505
37732
|
if (args.continueFrom !== void 0 && !continued && !sharedContinuation) return toolResult(false, { error: `${args.continueFrom} was merged or discarded meanwhile` });
|
|
37506
37733
|
if (continued) session.worktrees.delete(args.continueFrom);
|
|
37507
|
-
root
|
|
37734
|
+
root[budget]++;
|
|
37508
37735
|
if (peer) root.peerCount++;
|
|
37509
|
-
const childId = `alp-child-${
|
|
37736
|
+
const childId = `alp-child-${randomUUID12()}`;
|
|
37510
37737
|
const startedAt = Date.now();
|
|
37511
37738
|
const assignment = { id: childId, agent: args.agent, mode: childMode, isolation, rootId, startedAt, warned: false, finished: false, ...writes(childMode) ? { writeCheckout: checkout } : {}, ...args.etaMinutes ? { eta: startedAt + args.etaMinutes * 6e4 } : {} };
|
|
37512
37739
|
if (sharedWriter && !holder) {
|
|
@@ -37664,12 +37891,15 @@ ${digest2}` : "")
|
|
|
37664
37891
|
const events = await waitFor(sessionId, session, (event2) => event2.assignment === childId && (event2.kind === "result" || event2.kind === "question"), void 0, true);
|
|
37665
37892
|
if (events === "user") return toolResult(true, { assignmentId: childId, agent: args.agent, status: "running", userMessage: true, next: USER_WROTE });
|
|
37666
37893
|
if (!events) {
|
|
37667
|
-
|
|
37668
|
-
|
|
37669
|
-
|
|
37670
|
-
|
|
37671
|
-
|
|
37672
|
-
|
|
37894
|
+
if (session.closed || session.wakeBlocked || !session.assignments.has(childId)) {
|
|
37895
|
+
return toolResult(false, {
|
|
37896
|
+
agent: args.agent,
|
|
37897
|
+
assignmentId: childId,
|
|
37898
|
+
status: "canceled",
|
|
37899
|
+
error: "Parent assignment stopped"
|
|
37900
|
+
});
|
|
37901
|
+
}
|
|
37902
|
+
return toolResult(false, { agent: args.agent, assignmentId: childId, status: "running", error: TURN_ENDED });
|
|
37673
37903
|
}
|
|
37674
37904
|
const event = events.find((candidate) => candidate.kind === "result") ?? events.find((candidate) => candidate.kind === "question");
|
|
37675
37905
|
if (!event) return toolResult(true, { assignmentId: childId, agent: args.agent, status: "running", events: events.map(publicEvent), next: CHECKED_IN });
|
|
@@ -37735,10 +37965,12 @@ ${digest2}` : "")
|
|
|
37735
37965
|
peerCount: 0,
|
|
37736
37966
|
assignments: /* @__PURE__ */ new Map(),
|
|
37737
37967
|
calls: 0,
|
|
37968
|
+
advisorCalls: 0,
|
|
37738
37969
|
mail: [],
|
|
37739
37970
|
waiters: [],
|
|
37740
37971
|
lastActivity: Date.now(),
|
|
37741
37972
|
wakes: 0,
|
|
37973
|
+
wakeFailures: 0,
|
|
37742
37974
|
wakeBlocked: false,
|
|
37743
37975
|
toolCalls: /* @__PURE__ */ new Map(),
|
|
37744
37976
|
acknowledged: Promise.resolve(),
|
|
@@ -38043,7 +38275,7 @@ Write scope: change files only under ${session.spec.writeRoot}. Your working dir
|
|
|
38043
38275
|
runLog(entry.rootId, { event: "assignment.finished", assignmentId: entry.assignmentId, reconciled: true, ...result });
|
|
38044
38276
|
if (parent && !parent.closed) post(entry.parentId, { kind: "result", from: entry.agent, assignment: entry.assignmentId, result });
|
|
38045
38277
|
}
|
|
38046
|
-
async function recoverTree(rootId, continueRoot) {
|
|
38278
|
+
async function recoverTree(rootId, continueRoot, parkedRoot) {
|
|
38047
38279
|
const root = sessions.get(rootId);
|
|
38048
38280
|
if (!root || root.closed) throw new Error(`Session ${rootId} is not open`);
|
|
38049
38281
|
const entries2 = inherited.filter((entry) => entry.rootId === rootId).sort((a, b) => a.ancestry.length - b.ancestry.length);
|
|
@@ -38072,7 +38304,7 @@ Write scope: change files only under ${session.spec.writeRoot}. Your working dir
|
|
|
38072
38304
|
if (outcome) outcome.outcome = "parked";
|
|
38073
38305
|
return;
|
|
38074
38306
|
}
|
|
38075
|
-
void startPrompt(sessionId, { clientMessageId: `alp-recover-${
|
|
38307
|
+
void startPrompt(sessionId, { clientMessageId: `alp-recover-${randomUUID12()}`, delivery: "auto", content: [{ type: "text", text: text3 }] }, session.parent ? "assignment" : "wake").catch((error51) => session.settle?.("failed", error51));
|
|
38076
38308
|
};
|
|
38077
38309
|
for (const entry of entries2) {
|
|
38078
38310
|
const child = sessions.get(entry.assignmentId);
|
|
@@ -38080,7 +38312,11 @@ Write scope: change files only under ${session.spec.writeRoot}. Your working dir
|
|
|
38080
38312
|
runLog(rootId, { event: "assignment.recovered", assignmentId: entry.assignmentId, agent: entry.agent, ...entry.taskId ? { taskId: entry.taskId } : {} });
|
|
38081
38313
|
proceed(entry.assignmentId, child, `ALP: ${cause} while you worked on this assignment, and reopened this session. Your earlier work in it${entry.worktree ? " and your worktree" : ""} is intact; check where you stopped (for example with git status), then continue. Finish with alp_handoff as before.${listed(resumed.get(entry.assignmentId))}`);
|
|
38082
38314
|
}
|
|
38083
|
-
if (
|
|
38315
|
+
if (parkedRoot) {
|
|
38316
|
+
root.parked = { reason: parkedRoot, since: Date.now() };
|
|
38317
|
+
emit(rootId, { type: "session.updated", session: snapshot(rootId, root) });
|
|
38318
|
+
if (options2.autoResume !== false && options2.recoveryResume !== false && !pauseOf(root.runtimeKind)) continueParked(rootId, root);
|
|
38319
|
+
} else if (continueRoot) proceed(rootId, root, `ALP: ${cause} during your turn, and reopened this session. Continue the user's request where you stopped.${listed(resumed.get(rootId))}`);
|
|
38084
38320
|
else for (const assignment of resumed.get(rootId) ?? []) post(rootId, { kind: "note", from: assignment.agent, assignment: assignment.id, passive: true, body: `${cause}; ALP reopened this assignment and it continues. It reports when done.` });
|
|
38085
38321
|
const failed = outcomes.filter((outcome) => outcome.outcome === "failed");
|
|
38086
38322
|
if (entries2.length || continueRoot) {
|
|
@@ -38127,6 +38363,11 @@ Write scope: change files only under ${session.spec.writeRoot}. Your working dir
|
|
|
38127
38363
|
const activeTurn = session.active;
|
|
38128
38364
|
session.wakeBlocked = true;
|
|
38129
38365
|
session.userStopped = true;
|
|
38366
|
+
session.parked = void 0;
|
|
38367
|
+
session.parkReason = void 0;
|
|
38368
|
+
emit(sessionId, { type: "session.updated", session: snapshot(sessionId, session) });
|
|
38369
|
+
approvalCancellation.set(session, (approvalCancellation.get(session) ?? 0) + 1);
|
|
38370
|
+
cancelQuestions(sessionId);
|
|
38130
38371
|
if (activeTurn && session.runtimeKind === "codex" && session.threadId) {
|
|
38131
38372
|
const partial2 = [...session.text.values()].join("\n").trim();
|
|
38132
38373
|
if (partial2) interruptedThreads.set(session.threadId, partial2.slice(-4e3));
|
|
@@ -38199,11 +38440,9 @@ Write scope: change files only under ${session.spec.writeRoot}. Your working dir
|
|
|
38199
38440
|
if (!wake) {
|
|
38200
38441
|
session.handoff = void 0;
|
|
38201
38442
|
session.wakes = 0;
|
|
38443
|
+
session.wakeFailures = 0;
|
|
38202
38444
|
session.wakeBlocked = false;
|
|
38203
38445
|
session.userStopped = false;
|
|
38204
|
-
if (!session.parent) {
|
|
38205
|
-
session.calls = 0;
|
|
38206
|
-
}
|
|
38207
38446
|
}
|
|
38208
38447
|
}
|
|
38209
38448
|
try {
|
|
@@ -38245,6 +38484,10 @@ ${interrupted}`), text_elements: [] }] : [],
|
|
|
38245
38484
|
);
|
|
38246
38485
|
const turnId = prompt.delivery === "steer" ? session.active : result.turn.id;
|
|
38247
38486
|
for (const event of mail) event.deliveredTurn = turnId;
|
|
38487
|
+
if (!session.parent && origin === "user") {
|
|
38488
|
+
session.calls = 0;
|
|
38489
|
+
session.advisorCalls = 0;
|
|
38490
|
+
}
|
|
38248
38491
|
session.seen.add(
|
|
38249
38492
|
prompt.clientMessageId
|
|
38250
38493
|
);
|
|
@@ -38260,7 +38503,10 @@ ${interrupted}`), text_elements: [] }] : [],
|
|
|
38260
38503
|
});
|
|
38261
38504
|
if (origin === "user" && !session.parent) session.userWaiting ??= Date.now();
|
|
38262
38505
|
if (session.supervisor && origin === "user") session.journal.push(stamp(`user ${prompt.delivery === "steer" ? "steered" : "asked"} ${session.mapping.agent.name}: ${clip(text3, 1500)}`));
|
|
38263
|
-
if (prompt.delivery === "steer" && origin === "user" && !session.parent)
|
|
38506
|
+
if (prompt.delivery === "steer" && origin === "user" && !session.parent) {
|
|
38507
|
+
releaseWaiters(session);
|
|
38508
|
+
releaseQuestions(sessionId);
|
|
38509
|
+
}
|
|
38264
38510
|
emit(sessionId, {
|
|
38265
38511
|
type: "prompt.accepted",
|
|
38266
38512
|
clientMessageId: prompt.clientMessageId,
|
|
@@ -38301,12 +38547,19 @@ ${interrupted}`), text_elements: [] }] : [],
|
|
|
38301
38547
|
return opened;
|
|
38302
38548
|
}),
|
|
38303
38549
|
prompt: (sessionId, input) => enqueue(async () => {
|
|
38550
|
+
const session = sessions.get(sessionId);
|
|
38551
|
+
const parked = session?.parked;
|
|
38304
38552
|
try {
|
|
38305
|
-
|
|
38553
|
+
const prompt = session?.active && session.resumedTurn === session.active && input.delivery === "auto" ? { ...input, delivery: "steer" } : input;
|
|
38554
|
+
await startPrompt(sessionId, prompt, "user");
|
|
38555
|
+
if (session && parked && session.parked === parked) {
|
|
38556
|
+
session.parked = void 0;
|
|
38557
|
+
emit(sessionId, { type: "session.updated", session: snapshot(sessionId, session) });
|
|
38558
|
+
}
|
|
38306
38559
|
} catch (error51) {
|
|
38307
|
-
const
|
|
38308
|
-
if (
|
|
38309
|
-
|
|
38560
|
+
const session2 = sessions.get(sessionId);
|
|
38561
|
+
if (session2?.seen.has(input.clientMessageId)) return;
|
|
38562
|
+
session2?.seen.add(input.clientMessageId);
|
|
38310
38563
|
emit(sessionId, { type: "prompt.failed", clientMessageId: input.clientMessageId, error: errorData(error51) });
|
|
38311
38564
|
}
|
|
38312
38565
|
}),
|
|
@@ -38450,8 +38703,8 @@ ${interrupted}`), text_elements: [] }] : [],
|
|
|
38450
38703
|
recoverable() {
|
|
38451
38704
|
return [...inherited];
|
|
38452
38705
|
},
|
|
38453
|
-
recover(rootId, { continueRoot = false } = {}) {
|
|
38454
|
-
return enqueue(() => recoverTree(rootId, continueRoot));
|
|
38706
|
+
recover(rootId, { continueRoot = false, parkedRoot } = {}) {
|
|
38707
|
+
return enqueue(() => recoverTree(rootId, continueRoot, parkedRoot));
|
|
38455
38708
|
},
|
|
38456
38709
|
async abandon(rootId, reason) {
|
|
38457
38710
|
const entries2 = inherited.filter((entry) => entry.rootId === rootId);
|
|
@@ -38484,13 +38737,13 @@ ${interrupted}`), text_elements: [] }] : [],
|
|
|
38484
38737
|
|
|
38485
38738
|
// packages/daemon/src/server.ts
|
|
38486
38739
|
import { createHash as createHash11, randomBytes as randomBytes7 } from "node:crypto";
|
|
38487
|
-
import { appendFile as appendFile3, chmod, mkdir as mkdir14, readFile as readFile19, unlink as unlink4, lstat as
|
|
38740
|
+
import { appendFile as appendFile3, chmod, mkdir as mkdir14, readFile as readFile19, unlink as unlink4, lstat as lstat8, rename as rename8 } from "node:fs/promises";
|
|
38488
38741
|
import net3 from "node:net";
|
|
38489
38742
|
import os9 from "node:os";
|
|
38490
38743
|
import path35 from "node:path";
|
|
38491
38744
|
|
|
38492
38745
|
// packages/protocol/src/contract-v1.js
|
|
38493
|
-
var METHODS_V1 = { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://alp.dev/schemas/methods-v1.json", "protocolVersion": 1, "errors": { "parseError": -32700, "invalidRequest": -32600, "methodNotFound": -32601, "invalidParams": -32602, "failed": 1e3, "notFound": 1001, "forbidden": 1003, "conflict": 1004, "unavailable": 1005, "protocol": 1006 }, "capabilities": { "attach.after": "session.attach accepts `after` and answers `cursor`/`reset`; envelopes kept in a tree timeline carry `cursor`.", "prompt.receipts": "session.prompt is answered at most once per (sessionId, clientMessageId) from a durable receipt; repeats answer the first outcome or outcome_unknown.", "extension.methods": "extension.register/unregister, and the daemon sends extension.invoke requests to the owning connection.", "team": "team.list: the teams a project can use, from the daemon host's built-ins, library and project.", "task": "task.list, task.create, task.update and task.gate.resolve on a project's .alp/tasks, written by the daemon under the task lock.", "library": "library.list/get/save/delete/duplicate/rename, library.skills.set and library.models on the daemon user's library and a project's .alp overrides; secrets are redacted on read.", "settings.language": "settings.language.get/set: the language the daemon user reads, in the daemon's ALP_HOME/settings.json.", "session.list.page": "session.list accepts `limit`, `cursor` and `query` and answers `nextCursor` while more sessions follow; without them it answers every session as before.", "question.watch": "question.watch: the connection is told of `question` and `question.resolved` in every tree (of a projectRoot when given), attached or not.", "question.watch.turns": "question.watch accepts `turns`: the connection is also told of `turn.ended` of every root in the trees it watches.", "workspace.files": "workspace.files.search: files under a directory whose relative path matches a query; read-only, names only, links never followed.", "workspace.files.read": "workspace.files.list and workspace.files.read: the entries of a directory and the content of a UTF-8 text file (at most 1 MiB) under a directory; read-only, links never followed, .git, .env* and key or credential files refused, .gitignore'd paths hidden unless asked.", "prompt.images": 'session.prompt takes inline image parts { type: "image", mimeType, data } (base64, no paths or URLs) within the image limits; a session snapshot says whether its runtime takes them in promptImages.' }, "methods": { "assignment.recall": { "x-alp": { "effect": "mutation", "scope": { "kind": "project", "param": "projectRoot", "required": false, "alternatives": ["assignmentId", "taskId"] }, "idempotent": false, "risks": ["model call"] }, "params": { "type": "object", "properties": { "assignmentId": { "type": "string" }, "taskId": { "type": "string" }, "projectRoot": { "type": "string" }, "question": { "type": "string" } }, "required": ["question"], "additionalProperties": true }, "result": { "type": "object", "properties": { "assignmentId": { "type": "string" }, "agent": { "type": "string" }, "taskId": { "type": "string" }, "finishedAt": { "type": "string" }, "answer": { "type": "string" } }, "required": ["assignmentId", "agent", "finishedAt", "answer"], "additionalProperties": true } }, "board.list": { "x-alp": { "effect": "query", "scope": { "kind": "project", "param": "projectRoot", "required": true }, "idempotent": true }, "params": { "type": "object", "properties": { "projectRoot": { "type": "string" } }, "required": ["projectRoot"], "additionalProperties": true }, "result": { "type": "object", "properties": { "pins": { "type": "array", "items": { "$ref": "#/$defs/pin" } } }, "required": ["pins"], "additionalProperties": true } }, "catalog.get": { "x-alp": { "effect": "query", "scope": { "kind": "host" }, "idempotent": true }, "params": { "type": "object", "properties": {}, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": { "models": { "type": "array", "items": { "type": "object" } }, "modes": { "type": "array", "items": { "type": "object" } }, "thinkingOptions": {}, "defaultModel": { "type": "string" } }, "required": ["models", "modes", "defaultModel"], "additionalProperties": true } }, "daemon.hello": { "x-alp": { "effect": "query", "scope": { "kind": "connection" }, "idempotent": true }, "params": { "type": "object", "properties": { "protocolVersion": { "const": 1 }, "client": { "type": "object", "properties": { "name": { "type": "string" }, "version": { "type": "string" } }, "required": ["name", "version"], "additionalProperties": true }, "capabilities": { "type": "array", "items": { "type": "string" } } }, "required": ["protocolVersion"], "additionalProperties": true }, "result": { "type": "object", "properties": { "protocolVersion": { "const": 1 }, "daemonVersion": { "type": "string" }, "pid": { "type": "integer" }, "hostname": { "type": "string" }, "capabilities": { "type": "array", "items": { "type": "string" } } }, "required": ["protocolVersion"], "additionalProperties": true } }, "daemon.pause": { "x-alp": { "effect": "mutation", "scope": { "kind": "host" }, "idempotent": true, "risks": ["admin"] }, "params": { "type": "object", "properties": { "runtime": { "enum": ["codex", "claude", "acp", "antigravity"] }, "now": { "type": "boolean" }, "reason": { "type": "string" } }, "required": [], "additionalProperties": true }, "result": { "$ref": "#/$defs/pauseState" } }, "daemon.pauses": { "x-alp": { "effect": "query", "scope": { "kind": "host" }, "idempotent": true }, "params": { "type": "object", "properties": {}, "required": [], "additionalProperties": true }, "result": { "$ref": "#/$defs/pauseState" } }, "daemon.resume": { "x-alp": { "effect": "mutation", "scope": { "kind": "host" }, "idempotent": true, "risks": ["admin"] }, "params": { "type": "object", "properties": { "runtime": { "enum": ["codex", "claude", "acp", "antigravity"] } }, "required": [], "additionalProperties": true }, "result": { "$ref": "#/$defs/pauseState" } }, "daemon.shutdown": { "x-alp": { "effect": "mutation", "scope": { "kind": "host" }, "idempotent": true, "risks": ["admin"] }, "params": { "type": "object", "properties": {}, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": {}, "required": [], "additionalProperties": true } }, "daemon.status": { "x-alp": { "effect": "query", "scope": { "kind": "host" }, "idempotent": true }, "params": { "type": "object", "properties": {}, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": { "pid": { "type": "integer" }, "startedAt": { "type": "string" }, "version": { "type": "string" }, "protocolVersion": { "type": "integer" }, "sessions": { "type": "integer" }, "questions": { "type": "integer" }, "previousExit": { "type": "object", "properties": { "kind": { "enum": ["clean", "crash"] }, "at": { "type": "string" } }, "required": ["kind"], "additionalProperties": true }, "persistence": { "type": "object" }, "health": { "$ref": "#/$defs/health" } }, "required": ["pid", "startedAt", "version", "protocolVersion", "sessions", "questions", "health"], "additionalProperties": true } }, "daemon.usage": { "x-alp": { "effect": "query", "scope": { "kind": "host" }, "idempotent": true, "risks": ["refresh asks every provider (spawns on the daemon host)"] }, "params": { "type": "object", "properties": { "refresh": { "type": "boolean" } }, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": { "providers": { "type": "array", "items": { "type": "object" } } }, "required": ["providers"], "additionalProperties": true } }, "extension.register": { "x-alp": { "effect": "mutation", "scope": { "kind": "connection" }, "idempotent": false, "risks": ["admin"] }, "params": { "type": "object", "properties": { "method": { "type": "string" } }, "required": ["method"], "additionalProperties": true }, "result": { "type": "object", "properties": { "registered": { "type": "string" } }, "required": ["registered"], "additionalProperties": true } }, "extension.unregister": { "x-alp": { "effect": "mutation", "scope": { "kind": "connection" }, "idempotent": false, "risks": ["admin"] }, "params": { "type": "object", "properties": { "method": { "type": "string" } }, "required": ["method"], "additionalProperties": true }, "result": { "type": "object", "properties": { "unregistered": { "type": "string" } }, "required": ["unregistered"], "additionalProperties": true } }, "library.delete": { "x-alp": { "effect": "mutation", "capability": "library", "scope": { "kind": "library-or-project", "param": "projectRoot", "selector": "scope" }, "idempotent": false }, "params": { "type": "object", "properties": { "kind": { "$ref": "#/$defs/libraryKind" }, "name": { "$ref": "#/$defs/entryName" }, "scope": { "$ref": "#/$defs/libraryScope" }, "projectRoot": { "$ref": "#/$defs/projectRoot" }, "revision": { "type": "string" } }, "required": ["kind", "name", "scope"], "additionalProperties": true }, "result": { "type": "object", "properties": { "removed": { "const": true }, "now": { "enum": ["builtin", "library"] } }, "required": ["removed"], "additionalProperties": true } }, "library.duplicate": { "x-alp": { "effect": "mutation", "capability": "library", "scope": { "kind": "library-or-project", "param": "projectRoot", "selector": "scope", "reads": ["builtin", "library"] }, "idempotent": false, "risks": ["exec-config when kind is mcp, hooks or providers"] }, "params": { "type": "object", "properties": { "kind": { "$ref": "#/$defs/libraryKind" }, "from": { "$ref": "#/$defs/entryName" }, "to": { "$ref": "#/$defs/entryName" }, "scope": { "$ref": "#/$defs/libraryScope" }, "projectRoot": { "$ref": "#/$defs/projectRoot" } }, "required": ["kind", "from", "to", "scope"], "additionalProperties": true }, "result": { "type": "object", "properties": { "revision": { "$ref": "#/$defs/libraryRevision" } }, "required": ["revision"], "additionalProperties": true } }, "library.get": { "x-alp": { "effect": "query", "capability": "library", "scope": { "kind": "project", "param": "projectRoot", "required": false, "reads": ["builtin", "library"] }, "idempotent": true, "redacts": ["mcp /server/env/*", "mcp /server/headers/*", "providers /provider/env/*"] }, "params": { "type": "object", "properties": { "kind": { "$ref": "#/$defs/libraryKind" }, "name": { "$ref": "#/$defs/entryName" }, "projectRoot": { "$ref": "#/$defs/projectRoot" }, "scope": { "$ref": "#/$defs/libraryScope" } }, "required": ["kind", "name"], "additionalProperties": true }, "result": { "type": "object", "properties": { "kind": { "$ref": "#/$defs/libraryKind" }, "name": { "type": "string" }, "source": { "$ref": "#/$defs/librarySource" }, "overrides": { "$ref": "#/$defs/librarySource" }, "content": { "type": "object" }, "revision": { "$ref": "#/$defs/libraryRevision" }, "usedBy": { "type": "array", "items": { "type": "string" } }, "librarySkills": { "type": "array", "items": { "type": "string" } }, "secrets": { "type": "array", "items": { "type": "string" } } }, "required": ["kind", "name", "source", "content", "revision", "usedBy"], "additionalProperties": true } }, "library.list": { "x-alp": { "effect": "query", "capability": "library", "scope": { "kind": "project", "param": "projectRoot", "required": false, "reads": ["builtin", "library"] }, "idempotent": true }, "params": { "type": "object", "properties": { "kind": { "$ref": "#/$defs/libraryKind" }, "projectRoot": { "$ref": "#/$defs/projectRoot" } }, "required": ["kind"], "additionalProperties": true }, "result": { "type": "object", "properties": { "projectRoot": { "type": ["string", "null"] }, "entries": { "type": "array", "items": { "$ref": "#/$defs/libraryEntryRow" } } }, "required": ["projectRoot", "entries"], "additionalProperties": true } }, "library.models": { "x-alp": { "effect": "query", "capability": "library", "scope": { "kind": "host" }, "idempotent": true }, "params": { "type": "object", "properties": {}, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": { "models": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "label": { "type": "string" }, "efforts": { "type": "array", "items": { "type": "string" } }, "effort": { "type": "string" } }, "required": ["id", "label", "efforts"], "additionalProperties": true } } }, "required": ["models"], "additionalProperties": true } }, "library.rename": { "x-alp": { "effect": "mutation", "capability": "library", "scope": { "kind": "library-or-project", "param": "projectRoot", "selector": "scope" }, "idempotent": false, "risks": ["exec-config when kind is mcp, hooks or providers"] }, "params": { "type": "object", "properties": { "kind": { "$ref": "#/$defs/libraryKind" }, "from": { "$ref": "#/$defs/entryName" }, "to": { "$ref": "#/$defs/entryName" }, "scope": { "$ref": "#/$defs/libraryScope" }, "projectRoot": { "$ref": "#/$defs/projectRoot" } }, "required": ["kind", "from", "to", "scope"], "additionalProperties": true }, "result": { "type": "object", "properties": { "revision": { "$ref": "#/$defs/libraryRevision" } }, "required": ["revision"], "additionalProperties": true } }, "library.save": { "x-alp": { "effect": "mutation", "capability": "library", "scope": { "kind": "library-or-project", "param": "projectRoot", "selector": "scope" }, "idempotent": "with revision (a repeat fails revision_conflict)", "risks": ["exec-config when kind is mcp, hooks or providers, or agents naming mcp/hooks/provider", "writes every known project's .alp/settings.json when kind is teams and sync is known-projects"] }, "params": { "type": "object", "properties": { "kind": { "$ref": "#/$defs/libraryKind" }, "name": { "$ref": "#/$defs/entryName" }, "scope": { "$ref": "#/$defs/libraryScope" }, "projectRoot": { "$ref": "#/$defs/projectRoot" }, "content": { "type": "object" }, "revision": { "$ref": "#/$defs/libraryRevision" }, "sync": { "enum": ["none", "known-projects"] } }, "required": ["kind", "name", "scope", "content"], "additionalProperties": true }, "result": { "type": "object", "properties": { "revision": { "$ref": "#/$defs/libraryRevision" }, "syncedProjects": { "type": "integer", "minimum": 0 }, "syncErrors": { "type": "array", "items": { "type": "string" } } }, "required": ["revision"], "additionalProperties": true } }, "library.skills.set": { "x-alp": { "effect": "mutation", "capability": "library", "scope": { "kind": "host" }, "writes": ["ALP_HOME/role-skills.json"], "idempotent": true }, "params": { "type": "object", "properties": { "agent": { "$ref": "#/$defs/entryName" }, "skills": { "type": "array", "items": { "$ref": "#/$defs/entryName" }, "maxItems": 100 } }, "required": ["agent", "skills"], "additionalProperties": true }, "result": { "type": "object", "properties": { "skills": { "type": "array", "items": { "type": "string" } } }, "required": ["skills"], "additionalProperties": true } }, "question.answer": { "x-alp": { "effect": "mutation", "scope": { "kind": "question", "param": "questionId", "prefix": true }, "idempotent": false }, "params": { "type": "object", "properties": { "questionId": { "type": "string" }, "text": { "type": "string" }, "dismiss": { "type": "boolean" }, "reason": { "type": "string" } }, "required": ["questionId"], "additionalProperties": true }, "result": { "type": "object", "properties": { "questionId": { "type": "string" } }, "required": ["questionId"], "additionalProperties": true } }, "question.list": { "x-alp": { "effect": "query", "scope": { "kind": "project", "param": "projectRoot", "required": false }, "idempotent": true }, "params": { "type": "object", "properties": { "projectRoot": { "type": "string" } }, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": { "questions": { "type": "array", "items": { "$ref": "#/$defs/question" } } }, "required": ["questions"], "additionalProperties": true } }, "question.watch": { "x-alp": { "effect": "query", "scope": { "kind": "project", "param": "projectRoot", "required": false }, "idempotent": true, "capability": "question.watch" }, "params": { "type": "object", "properties": { "projectRoot": { "type": "string" }, "turns": { "type": "boolean" } }, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": { "questions": { "type": "array", "items": { "$ref": "#/$defs/question" } } }, "required": ["questions"], "additionalProperties": true } }, "session.archive": { "x-alp": { "effect": "mutation", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" }, "archived": { "type": "boolean" } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": { "session": { "$ref": "#/$defs/sessionSummary" } }, "required": ["session"], "additionalProperties": true } }, "session.attach": { "x-alp": { "effect": "query", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" }, "replay": { "type": "boolean" }, "after": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": { "session": { "$ref": "#/$defs/sessionSummary" }, "cursor": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "reset": { "const": true } }, "required": ["session", "cursor"], "additionalProperties": true } }, "session.close": { "x-alp": { "effect": "mutation", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": false }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": {}, "required": [], "additionalProperties": true } }, "session.configure": { "x-alp": { "effect": "mutation", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" }, "mode": { "type": "string" } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": { "session": { "$ref": "#/$defs/session" } }, "required": ["session"], "additionalProperties": true } }, "session.create": { "x-alp": { "effect": "mutation", "scope": { "kind": "project", "param": "spec.cwd" }, "idempotent": false, "risks": ["exec: spec.env and stdio spec.mcpServers run on the daemon host", "spec.workdir/writeRoot/copy*/restore/keepThread are internal"] }, "params": { "type": "object", "properties": { "sessionId": { "type": "string", "pattern": "^[A-Za-z0-9_.:-]{1,128}$" }, "spec": { "$ref": "#/$defs/spec" }, "history": { "enum": ["skip", "replay"] }, "delegation": { "type": "boolean" }, "resume": { "type": "boolean" } }, "required": ["spec"], "additionalProperties": true }, "result": { "type": "object", "properties": { "session": { "$ref": "#/$defs/session" }, "attached": { "type": "boolean" }, "resumed": { "const": true }, "durable": { "type": "boolean" } }, "required": ["session", "attached"], "additionalProperties": true } }, "session.get": { "x-alp": { "effect": "query", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": { "session": { "$ref": "#/$defs/sessionSummary" } }, "required": ["session"], "additionalProperties": true } }, "session.interrupt": { "x-alp": { "effect": "mutation", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": {}, "required": [], "additionalProperties": true } }, "session.list": { "x-alp": { "effect": "query", "scope": { "kind": "project", "param": "projectRoot", "required": false }, "idempotent": true }, "params": { "type": "object", "properties": { "projectRoot": { "type": "string" }, "rootsOnly": { "type": "boolean" }, "includeClosed": { "type": "boolean" }, "includeArchived": { "type": "boolean" }, "limit": { "type": "integer", "minimum": 1, "maximum": 1e3 }, "cursor": { "type": "string", "minLength": 1, "maxLength": 1024 }, "query": { "type": "string", "maxLength": 200 }, "projectRoots": { "type": "array", "items": { "type": "string" }, "minItems": 1, "maxItems": 1024 } }, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": { "sessions": { "type": "array", "items": { "$ref": "#/$defs/sessionSummary" } }, "nextCursor": { "type": "string", "minLength": 1 } }, "required": ["sessions"], "additionalProperties": true } }, "session.log": { "x-alp": { "effect": "query", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": { "rootId": { "type": "string" }, "entries": { "type": "array", "items": { "type": "object" } } }, "required": ["rootId", "entries"], "additionalProperties": true } }, "session.message": { "x-alp": { "effect": "mutation", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": false }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" }, "text": { "type": "string" } }, "required": ["sessionId", "text"], "additionalProperties": true }, "result": { "type": "object", "properties": {}, "required": [], "additionalProperties": true } }, "session.preview": { "x-alp": { "effect": "query", "scope": { "kind": "project", "param": "spec.cwd" }, "idempotent": true }, "params": { "type": "object", "properties": { "spec": { "$ref": "#/$defs/spec" } }, "required": ["spec"], "additionalProperties": true }, "result": { "type": "object", "properties": { "preview": { "$ref": "#/$defs/preview" } }, "required": ["preview"], "additionalProperties": true } }, "session.prompt": { "x-alp": { "effect": "mutation", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": "with prompt.receipts" }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" }, "clientMessageId": { "type": "string" }, "delivery": { "enum": ["auto", "steer"] }, "content": { "type": "array", "items": { "anyOf": [{ "type": "object", "properties": { "type": { "const": "text" }, "text": { "type": "string" } }, "required": ["type"], "additionalProperties": true }, { "$ref": "#/$defs/promptImage" }, { "type": "object", "properties": { "type": { "type": "string", "not": { "enum": ["text", "image"] } } }, "required": ["type"], "additionalProperties": true }] } } }, "required": ["sessionId", "clientMessageId", "content"], "additionalProperties": true }, "result": { "type": "object", "properties": { "outcome": { "enum": ["accepted", "failed"] }, "error": { "$ref": "#/$defs/alpError" }, "duplicate": { "type": "boolean" }, "durable": { "type": "boolean" } }, "required": [], "additionalProperties": true } }, "session.release": { "x-alp": { "effect": "mutation", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": { "closed": { "type": "boolean" } }, "required": ["closed"], "additionalProperties": true } }, "session.rename": { "x-alp": { "effect": "mutation", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" }, "title": { "type": "string", "minLength": 1, "maxLength": 200, "pattern": "\\S" } }, "required": ["sessionId", "title"], "additionalProperties": true }, "result": { "type": "object", "properties": { "session": { "$ref": "#/$defs/sessionSummary" } }, "required": ["session"], "additionalProperties": true } }, "session.status": { "x-alp": { "effect": "query", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": { "status": { "$ref": "#/$defs/treeStatus" } }, "required": ["status"], "additionalProperties": true } }, "settings.language.get": { "x-alp": { "effect": "query", "capability": "settings.language", "scope": { "kind": "host" }, "idempotent": true }, "params": { "type": "object", "properties": {}, "required": [], "additionalProperties": true }, "result": { "$ref": "#/$defs/languageSetting" } }, "settings.language.set": { "x-alp": { "effect": "mutation", "capability": "settings.language", "scope": { "kind": "host" }, "writes": ["ALP_HOME/settings.json"], "idempotent": true }, "params": { "type": "object", "properties": { "language": { "anyOf": [{ "type": "string", "minLength": 1, "maxLength": 40, "pattern": "\\S" }, { "type": "null" }] } }, "required": ["language"], "additionalProperties": true }, "result": { "$ref": "#/$defs/languageSetting" } }, "task.create": { "x-alp": { "effect": "mutation", "capability": "task", "scope": { "kind": "project", "param": "projectRoot", "required": true }, "writes": ["<projectRoot>/.alp/tasks"], "idempotent": "per requestId within one alpd process (10 minutes)" }, "params": { "type": "object", "properties": { "projectRoot": { "$ref": "#/$defs/projectRoot" }, "title": { "type": "string", "minLength": 1, "maxLength": 200, "pattern": "\\S" }, "priority": { "type": "integer", "minimum": 0, "maximum": 4 }, "requestId": { "type": "string", "minLength": 1, "maxLength": 128 } }, "required": ["projectRoot", "title"], "additionalProperties": true }, "result": { "type": "object", "properties": { "id": { "$ref": "#/$defs/taskId" }, "duplicate": { "const": true } }, "required": ["id"], "additionalProperties": true } }, "task.gate.resolve": { "x-alp": { "effect": "mutation", "capability": "task", "scope": { "kind": "project", "param": "projectRoot", "required": true }, "writes": ["<projectRoot>/.alp/tasks"], "idempotent": false }, "params": { "type": "object", "properties": { "projectRoot": { "$ref": "#/$defs/projectRoot" }, "id": { "$ref": "#/$defs/taskId" }, "gate": { "type": "string", "minLength": 1, "maxLength": 128 }, "note": { "type": "string", "maxLength": 2e3 }, "ifRev": { "type": "integer", "minimum": 0 } }, "required": ["projectRoot", "id", "gate"], "additionalProperties": true }, "result": { "type": "object", "properties": { "id": { "$ref": "#/$defs/taskId" }, "status": { "type": "string" }, "rev": { "type": "integer", "minimum": 0 } }, "required": ["id", "status", "rev"], "additionalProperties": true } }, "task.list": { "x-alp": { "effect": "query", "capability": "task", "scope": { "kind": "project", "param": "projectRoot", "required": true }, "idempotent": true }, "params": { "type": "object", "properties": { "projectRoot": { "$ref": "#/$defs/projectRoot" } }, "required": ["projectRoot"], "additionalProperties": true }, "result": { "type": "object", "properties": { "projectRoot": { "type": "string" }, "tasks": { "type": "array", "items": { "$ref": "#/$defs/taskRow" } }, "unreadable": { "type": "array", "items": { "type": "object", "properties": { "file": { "type": "string" }, "error": { "type": "string" } }, "required": ["file", "error"], "additionalProperties": true } }, "revision": { "type": "string" } }, "required": ["projectRoot", "tasks", "unreadable", "revision"], "additionalProperties": true } }, "task.update": { "x-alp": { "effect": "mutation", "capability": "task", "scope": { "kind": "project", "param": "projectRoot", "required": true }, "writes": ["<projectRoot>/.alp/tasks"], "idempotent": false }, "params": { "type": "object", "properties": { "projectRoot": { "$ref": "#/$defs/projectRoot" }, "id": { "$ref": "#/$defs/taskId" }, "action": { "enum": ["close", "reopen"] }, "note": { "type": "string", "maxLength": 2e3 }, "ifRev": { "type": "integer", "minimum": 0 } }, "required": ["projectRoot", "id", "action"], "additionalProperties": true }, "result": { "type": "object", "properties": { "id": { "$ref": "#/$defs/taskId" }, "status": { "type": "string" }, "rev": { "type": "integer", "minimum": 0 }, "landed": { "type": "string" } }, "required": ["id", "status", "rev"], "additionalProperties": true } }, "team.list": { "x-alp": { "effect": "query", "capability": "team", "scope": { "kind": "project", "param": "projectRoot", "required": false, "reads": ["builtin", "library"] }, "idempotent": true }, "params": { "type": "object", "properties": { "projectRoot": { "$ref": "#/$defs/projectRoot" } }, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": { "projectRoot": { "type": ["string", "null"] }, "teams": { "type": "array", "items": { "$ref": "#/$defs/teamRow" } } }, "required": ["projectRoot", "teams"], "additionalProperties": true } }, "workspace.files.search": { "x-alp": { "effect": "query", "scope": { "kind": "project", "param": "projectRoot", "required": true }, "idempotent": true, "capability": "workspace.files", "risks": ["lists file names of the directory (not contents)"] }, "params": { "type": "object", "properties": { "projectRoot": { "type": "string" }, "query": { "type": "string", "maxLength": 200 }, "limit": { "type": "integer", "minimum": 1, "maximum": 100 } }, "required": ["projectRoot"], "additionalProperties": true }, "result": { "type": "object", "properties": { "files": { "type": "array", "maxItems": 100, "items": { "type": "object", "properties": { "path": { "type": "string", "minLength": 1 } }, "required": ["path"], "additionalProperties": true } }, "truncated": { "type": "boolean" } }, "required": ["files"], "additionalProperties": true } }, "workspace.files.list": { "x-alp": { "effect": "query", "scope": { "kind": "project", "param": "projectRoot", "required": true }, "idempotent": true, "capability": "workspace.files.read", "risks": ["lists names, kinds, sizes and times of a directory's entries (not contents)"] }, "params": { "type": "object", "properties": { "projectRoot": { "type": "string" }, "path": { "type": "string", "minLength": 1, "maxLength": 4096 }, "includeIgnored": { "type": "boolean" } }, "required": ["projectRoot"], "additionalProperties": true }, "result": { "type": "object", "properties": { "entries": { "type": "array", "maxItems": 1e3, "items": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1 }, "path": { "type": "string", "minLength": 1 }, "kind": { "enum": ["file", "directory"] }, "size": { "type": "integer", "minimum": 0 }, "modifiedAt": { "type": "string" } }, "required": ["name", "path", "kind"], "additionalProperties": true } }, "truncated": { "type": "boolean" } }, "required": ["entries"], "additionalProperties": true } }, "workspace.files.read": { "x-alp": { "effect": "query", "scope": { "kind": "project", "param": "projectRoot", "required": true }, "idempotent": true, "capability": "workspace.files.read", "risks": ["returns the content of a text file under the directory"] }, "params": { "type": "object", "properties": { "projectRoot": { "type": "string" }, "path": { "type": "string", "minLength": 1, "maxLength": 4096 }, "includeIgnored": { "type": "boolean" } }, "required": ["projectRoot", "path"], "additionalProperties": true }, "result": { "type": "object", "properties": { "path": { "type": "string", "minLength": 1 }, "content": { "type": "string" }, "size": { "type": "integer", "minimum": 0, "maximum": 1048576 }, "modifiedAt": { "type": "string" } }, "required": ["path", "content", "size"], "additionalProperties": true } } }, "$defs": { "error": { "type": "object", "properties": { "code": { "type": "integer" }, "message": { "type": "string" }, "data": {} }, "required": ["code", "message"], "additionalProperties": true }, "session": { "type": "object", "properties": { "id": { "type": "string" }, "projectRoot": { "type": "string" }, "agent": { "type": "string" }, "runtime": { "enum": ["claude", "codex", "acp", "antigravity"] }, "model": { "type": "string" }, "mode": { "type": "string" }, "thinking": { "type": "string" }, "workflow": { "type": "object", "properties": { "mode": { "type": "string" }, "maxPeers": { "type": "integer" }, "supervisor": { "type": "boolean" } }, "required": ["mode", "maxPeers", "supervisor"], "additionalProperties": true }, "threadId": { "type": "string" }, "persistent": { "type": "boolean" }, "busy": { "type": "boolean" }, "parentId": { "type": "string" }, "toolCallId": { "type": "string" }, "activeTurnId": { "type": "string" }, "parked": { "type": "string" }, "instructionsSha": { "type": "string" }, "teamLabel": { "type": "string" }, "promptImages": { "type": "boolean" }, "usage": { "$ref": "#/$defs/usage" } }, "required": ["id", "projectRoot", "agent", "runtime", "model", "mode", "thinking", "workflow", "threadId", "persistent", "busy"], "additionalProperties": true }, "usage": { "type": "object", "properties": { "contextWindowUsedTokens": { "type": "integer", "minimum": 0 }, "contextWindowMaxTokens": { "type": "integer", "minimum": 1 }, "inputTokens": { "type": "integer", "minimum": 0 }, "cachedInputTokens": { "type": "integer", "minimum": 0 }, "outputTokens": { "type": "integer", "minimum": 0 }, "totalCostUsd": { "type": "number", "minimum": 0 } }, "additionalProperties": true }, "assignment": { "type": "object", "properties": { "id": { "type": "string" }, "agent": { "type": "string" }, "mode": { "type": "string" }, "status": { "type": "string" }, "startedAt": { "type": "string" }, "worktree": { "type": "object", "properties": { "branch": { "type": "string" }, "path": { "type": "string" } }, "required": ["branch", "path"], "additionalProperties": true } }, "required": ["id", "agent", "mode", "status", "startedAt"], "additionalProperties": true }, "mail": { "type": "object", "properties": { "id": { "type": "string" }, "kind": { "enum": ["question", "answer", "note", "steer", "result", "stalled", "board", "checkin"] }, "from": { "type": "string" }, "assignment": { "type": "string" }, "body": { "type": "string" }, "replyTo": { "type": "string" }, "result": { "type": "object" }, "redelivered": { "type": "boolean" } }, "required": ["id", "kind", "from", "assignment"], "additionalProperties": true }, "pin": { "type": "object", "properties": { "id": { "type": "string" }, "project": { "type": "string" }, "kind": { "enum": ["claim", "decision", "finding"] }, "body": { "type": "string" }, "paths": { "type": "array", "items": { "type": "string" } }, "agent": { "type": "string" }, "sessionId": { "type": "string" }, "rootId": { "type": "string" }, "at": { "type": "string" }, "released": { "type": "string" }, "task": { "type": "string" } }, "required": ["id", "project", "kind", "body", "agent", "sessionId", "rootId", "at"], "additionalProperties": true }, "question": { "type": "object", "properties": { "id": { "type": "string" }, "sessionId": { "type": "string" }, "rootId": { "type": "string" }, "agent": { "type": "string" }, "body": { "type": "string" }, "options": { "type": "array", "items": { "type": "string" } }, "askedAt": { "type": "string" } }, "required": ["id", "sessionId", "rootId", "agent", "body", "askedAt"], "additionalProperties": true }, "spec": { "type": "object", "properties": { "cwd": { "type": "string" }, "agent": { "type": "string" }, "model": { "type": "string" }, "mode": { "type": "string" }, "thinking": { "type": "string" }, "persist": { "type": "boolean" }, "restore": { "type": "object" } }, "required": ["cwd"], "additionalProperties": true }, "item": { "anyOf": [{ "$ref": "#/$defs/knownItem" }, { "$ref": "#/$defs/unknownItem" }] }, "event": { "anyOf": [{ "$ref": "#/$defs/knownEvent" }, { "$ref": "#/$defs/unknownEvent" }] }, "envelope": { "type": "object", "properties": { "sessionId": { "type": "string" }, "epoch": { "type": "string" }, "seq": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "ts": { "type": "string" }, "event": { "$ref": "#/$defs/event" }, "cursor": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 } }, "required": ["sessionId", "epoch", "seq", "ts", "event"], "additionalProperties": true }, "request": { "type": "object", "properties": { "jsonrpc": { "const": "2.0" }, "id": { "anyOf": [{ "type": "string", "maxLength": 128 }, { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }] }, "method": { "type": "string" }, "params": { "type": "object" } }, "required": ["jsonrpc", "method", "id"], "additionalProperties": true }, "response": { "oneOf": [{ "type": "object", "properties": { "jsonrpc": { "const": "2.0" }, "id": { "anyOf": [{ "type": "string", "maxLength": 128 }, { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" }] }, "result": {} }, "required": ["jsonrpc", "id", "result"], "additionalProperties": true }, { "type": "object", "properties": { "jsonrpc": { "const": "2.0" }, "id": { "anyOf": [{ "type": "string", "maxLength": 128 }, { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" }] }, "error": { "$ref": "#/$defs/error" } }, "required": ["jsonrpc", "id", "error"], "additionalProperties": true }] }, "notification": { "type": "object", "properties": { "jsonrpc": { "const": "2.0" }, "method": { "const": "event" }, "params": { "$ref": "#/$defs/envelope" } }, "required": ["jsonrpc", "method", "params"], "additionalProperties": true }, "knownEvent": { "oneOf": [{ "type": "object", "properties": { "type": { "const": "session.opened" }, "session": { "$ref": "#/$defs/session" }, "cwd": { "type": "string" }, "effective": { "type": "object", "properties": { "model": { "type": "string" }, "thinking": { "type": "string" } }, "required": ["model", "thinking"], "additionalProperties": true } }, "required": ["type", "session", "cwd", "effective"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "session.ready" } }, "required": ["type"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "session.updated" }, "session": { "$ref": "#/$defs/session" } }, "required": ["type", "session"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "session.closed" } }, "required": ["type"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "session.failed" }, "error": { "$ref": "#/$defs/alpError" } }, "required": ["type", "error"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "prompt.accepted" }, "clientMessageId": { "type": "string" }, "result": { "enum": ["turn", "steer"] }, "turnId": { "type": "string" } }, "required": ["type", "clientMessageId", "result", "turnId"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "prompt.failed" }, "clientMessageId": { "type": "string" }, "error": { "$ref": "#/$defs/alpError" } }, "required": ["type", "clientMessageId", "error"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "turn.started" }, "turnId": { "type": "string" }, "origin": { "enum": ["user", "wake", "assignment", "runtime"] } }, "required": ["type", "turnId", "origin"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "turn.ended" }, "turnId": { "type": "string" }, "state": { "enum": ["completed", "failed", "canceled"] }, "error": { "$ref": "#/$defs/alpError" } }, "required": ["type", "turnId", "state"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "item" }, "item": { "$ref": "#/$defs/item" } }, "required": ["type", "item"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "usage" }, "usage": { "$ref": "#/$defs/usage" } }, "required": ["type", "usage"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "mail" }, "mail": { "$ref": "#/$defs/mail" } }, "required": ["type", "mail"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "assignment" }, "assignment": { "$ref": "#/$defs/assignment" } }, "required": ["type", "assignment"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "question" }, "question": { "$ref": "#/$defs/question" } }, "required": ["type", "question"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "question.resolved" }, "questionId": { "type": "string" }, "outcome": { "enum": ["answered", "dismissed", "timeout", "canceled"] }, "answer": { "type": "string" } }, "required": ["type", "questionId", "outcome"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "pin" }, "pin": { "$ref": "#/$defs/pin" } }, "required": ["type", "pin"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "unpin" }, "pinId": { "type": "string" }, "reason": { "enum": ["unpinned", "session_ended"] } }, "required": ["type", "pinId", "reason"], "additionalProperties": true }] }, "unknownEvent": { "type": "object", "properties": { "type": { "type": "string", "not": { "enum": ["session.opened", "session.ready", "session.updated", "session.closed", "session.failed", "prompt.accepted", "prompt.failed", "turn.started", "turn.ended", "item", "usage", "mail", "assignment", "question", "question.resolved", "pin", "unpin"] } } }, "required": ["type"], "additionalProperties": true }, "knownItem": { "oneOf": [{ "type": "object", "properties": { "kind": { "const": "user_message" }, "id": { "type": "string" }, "text": { "type": "string" }, "clientMessageId": { "type": "string" }, "images": { "type": "integer", "minimum": 1 } }, "required": ["kind", "id", "text"], "additionalProperties": true }, { "type": "object", "properties": { "kind": { "const": "assistant_message" }, "id": { "type": "string" }, "text": { "type": "string" } }, "required": ["kind", "id", "text"], "additionalProperties": true }, { "type": "object", "properties": { "kind": { "const": "notice" }, "id": { "type": "string" }, "level": { "enum": ["info", "warning", "error"] }, "text": { "type": "string" } }, "required": ["kind", "id", "level", "text"], "additionalProperties": true }, { "type": "object", "properties": { "kind": { "const": "compaction" }, "id": { "type": "string" }, "status": { "enum": ["running", "completed", "failed"] }, "trigger": { "type": "string" }, "preTokens": { "type": "integer" }, "postTokens": { "type": "integer" } }, "required": ["kind", "id", "status"], "additionalProperties": true }, { "type": "object", "properties": { "kind": { "const": "todo" }, "id": { "type": "string" }, "items": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "text": { "type": "string" }, "status": { "enum": ["pending", "in_progress", "completed"] } }, "required": ["id", "text", "status"], "additionalProperties": true } } }, "required": ["kind", "id", "items"], "additionalProperties": true }, { "type": "object", "properties": { "kind": { "const": "tool_call" }, "id": { "type": "string" }, "callId": { "type": "string" }, "name": { "type": "string" }, "status": { "enum": ["running", "completed", "failed"] }, "error": { "type": "string" }, "detail": { "$ref": "#/$defs/toolCallDetail" } }, "required": ["kind", "id", "callId", "name", "status", "detail"], "additionalProperties": true }] }, "unknownItem": { "type": "object", "properties": { "kind": { "type": "string", "not": { "enum": ["user_message", "assistant_message", "notice", "compaction", "todo", "tool_call"] } }, "id": { "type": "string" } }, "required": ["kind", "id"], "additionalProperties": true }, "toolCallDetail": { "oneOf": [{ "type": "object", "properties": { "type": { "const": "shell" }, "command": { "type": "string" }, "cwd": { "type": "string" }, "output": { "type": "string" }, "exitCode": { "type": ["integer", "null"] } }, "required": ["type", "command", "output"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "read" }, "filePath": { "type": "string" }, "content": { "type": "string" }, "offset": { "type": "integer" }, "limit": { "type": "integer" } }, "required": ["type", "filePath"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "edit" }, "filePath": { "type": "string" }, "oldString": { "type": "string" }, "newString": { "type": "string" }, "unifiedDiff": { "type": "string" } }, "required": ["type", "filePath"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "write" }, "filePath": { "type": "string" }, "content": { "type": "string" } }, "required": ["type", "filePath"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "search" }, "query": { "type": "string" }, "toolName": { "enum": ["search", "grep", "glob", "web_search"] }, "content": { "type": "string" }, "filePaths": { "type": "array", "items": { "type": "string" } }, "webResults": { "type": "array", "items": { "type": "object", "properties": { "title": { "type": "string" }, "url": { "type": "string" } }, "required": ["title", "url"], "additionalProperties": true } }, "numFiles": { "type": "integer" }, "numMatches": { "type": "integer" }, "truncated": { "type": "boolean" } }, "required": ["type", "query"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "fetch" }, "url": { "type": "string" }, "prompt": { "type": "string" }, "result": { "type": "string" }, "code": { "type": "integer" } }, "required": ["type", "url"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "sub_agent" }, "subAgentType": { "type": "string" }, "description": { "type": "string" }, "log": { "type": "string" } }, "required": ["type"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "plan" }, "text": { "type": "string" } }, "required": ["type", "text"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "plain_text" }, "label": { "type": "string" }, "text": { "type": "string" } }, "required": ["type"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "unknown" }, "input": {}, "output": {} }, "required": ["type", "input", "output"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "type": "string", "not": { "enum": ["shell", "read", "edit", "write", "search", "fetch", "sub_agent", "plan", "plain_text", "unknown"] } } }, "required": ["type"], "additionalProperties": true }] }, "alpError": { "type": "object", "properties": { "message": { "type": "string" }, "code": { "type": "string" } }, "required": ["message"], "additionalProperties": true }, "sessionSummary": { "type": "object", "properties": { "id": { "type": "string" }, "projectRoot": { "type": "string" }, "agent": { "type": "string" }, "runtime": { "enum": ["claude", "codex", "acp", "antigravity"] }, "model": { "type": "string" }, "mode": { "type": "string" }, "thinking": { "type": "string" }, "workflow": { "type": "object", "properties": { "mode": { "type": "string" }, "maxPeers": { "type": "integer" }, "supervisor": { "type": "boolean" } }, "required": ["mode", "maxPeers", "supervisor"], "additionalProperties": true }, "threadId": { "type": "string" }, "persistent": { "type": "boolean" }, "busy": { "type": "boolean" }, "parentId": { "type": "string" }, "toolCallId": { "type": "string" }, "activeTurnId": { "type": "string" }, "parked": { "type": "string" }, "instructionsSha": { "type": "string" }, "teamLabel": { "type": "string" }, "status": { "enum": ["initializing", "idle", "running", "error", "closed"] }, "title": { "type": "string" }, "archived": { "type": "boolean" }, "lastError": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" } }, "required": ["message"], "additionalProperties": true }, "createdAt": { "type": "string" }, "updatedAt": { "type": "string" } }, "required": ["id", "projectRoot", "agent", "runtime", "model", "mode", "thinking", "workflow", "threadId", "persistent", "busy", "status"], "additionalProperties": true }, "pause": { "type": "object", "properties": { "since": { "type": "string" }, "by": { "type": "string" }, "reason": { "type": "string" }, "resetsAt": { "type": "string" }, "checkedAt": { "type": "string" } }, "required": ["since", "by", "reason"], "additionalProperties": true }, "pauseState": { "type": "object", "properties": { "all": { "$ref": "#/$defs/pause" }, "runtimes": { "type": "object", "additionalProperties": { "$ref": "#/$defs/pause" } }, "parked": { "type": "array", "items": { "type": "object", "properties": { "assignmentId": { "type": "string" }, "agent": { "type": "string" }, "runtime": { "type": "string" }, "rootId": { "type": "string" }, "reason": { "type": "string" }, "since": { "type": "string" } }, "required": ["assignmentId", "agent", "runtime", "rootId", "reason", "since"], "additionalProperties": true } } }, "required": ["runtimes", "parked"], "additionalProperties": true }, "preview": { "type": "object", "properties": { "teams": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "label": { "type": "string" }, "description": { "type": "string" } }, "required": ["id", "label"], "additionalProperties": true } }, "team": { "type": "string" }, "teamLabel": { "type": "string" }, "agent": { "type": "string" }, "runtime": { "enum": ["claude", "codex", "acp", "antigravity"] }, "model": { "type": "string" }, "mode": { "type": "string" }, "thinking": { "type": "string" } }, "required": ["teams", "team", "agent", "runtime", "model", "mode", "thinking"], "additionalProperties": true }, "treeStatus": { "type": "object", "properties": { "rootId": { "type": "string" }, "sessions": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "projectRoot": { "type": "string" }, "agent": { "type": "string" }, "runtime": { "enum": ["claude", "codex", "acp", "antigravity"] }, "model": { "type": "string" }, "mode": { "type": "string" }, "thinking": { "type": "string" }, "workflow": { "type": "object", "properties": { "mode": { "type": "string" }, "maxPeers": { "type": "integer" }, "supervisor": { "type": "boolean" } }, "required": ["mode", "maxPeers", "supervisor"], "additionalProperties": true }, "threadId": { "type": "string" }, "persistent": { "type": "boolean" }, "busy": { "type": "boolean" }, "parentId": { "type": "string" }, "toolCallId": { "type": "string" }, "activeTurnId": { "type": "string" }, "parked": { "type": "string" }, "instructionsSha": { "type": "string" }, "teamLabel": { "type": "string" }, "state": { "enum": ["running", "waiting", "waiting_parent", "waiting_user", "idle"] }, "idleMs": { "type": "number" }, "workdir": { "type": "string" }, "unreadMail": { "type": "integer", "minimum": 0 } }, "required": ["id", "projectRoot", "agent", "runtime", "model", "mode", "thinking", "workflow", "threadId", "persistent", "busy", "state", "idleMs", "workdir", "unreadMail"], "additionalProperties": true } }, "assignments": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "requester": { "type": "string" }, "isolation": { "enum": ["shared", "worktree"] }, "idleMs": { "type": "number" } }, "required": ["id", "requester", "isolation", "idleMs"], "additionalProperties": true } }, "questions": { "type": "array", "items": { "$ref": "#/$defs/question" } }, "worktrees": { "type": "array", "items": { "type": "object", "properties": { "assignmentId": { "type": "string" }, "requester": { "type": "string" }, "agent": { "type": "string" }, "branch": { "type": "string" }, "files": { "type": "array", "items": { "type": "string" } }, "stat": { "type": "string" } }, "required": ["assignmentId", "requester", "agent", "branch", "files", "stat"], "additionalProperties": true } }, "leases": { "type": "array", "items": { "type": "object", "properties": { "checkout": { "type": "string" }, "assignmentId": { "type": "string" }, "agent": { "type": "string" } }, "required": ["checkout", "assignmentId", "agent"], "additionalProperties": true } }, "claims": { "type": "array", "items": { "$ref": "#/$defs/pin" } } }, "required": ["rootId", "sessions", "assignments", "questions", "worktrees", "leases", "claims"], "additionalProperties": true }, "health": { "type": "object", "properties": { "live": { "type": "boolean" }, "ready": { "type": "boolean" }, "checks": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "ok": { "type": "boolean" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": ["id", "ok"], "additionalProperties": true } } }, "required": ["live", "ready", "checks"], "additionalProperties": true }, "projectRoot": { "type": "string", "minLength": 1, "maxLength": 4096, "description": "An absolute, normalized path on the daemon host to a directory holding .alp/. Compared as is: the daemon never walks up to a parent." }, "taskId": { "type": "string", "pattern": "^t-[0-9a-f]{4,12}(?:\\.\\d+)*$" }, "taskRow": { "type": "object", "properties": { "id": { "$ref": "#/$defs/taskId" }, "title": { "type": "string" }, "type": { "type": "string" }, "priority": { "type": "integer" }, "status": { "enum": ["open", "in_progress", "review", "closed"] }, "rev": { "type": "integer", "minimum": 0 }, "parent": { "type": "string" }, "assignee": { "type": "string" }, "ready": { "type": "boolean" }, "blockedBy": { "type": "array", "items": { "type": "string" } }, "waits": { "type": "array", "items": { "type": "string" } }, "approvals": { "type": "array", "items": { "type": "object", "properties": { "gate": { "type": "string" }, "note": { "type": "string" } }, "required": ["gate", "note"], "additionalProperties": true } }, "handoff": { "type": "object", "properties": { "outcome": { "type": "string" }, "summary": { "type": "string" }, "agent": { "type": "string" } }, "required": ["outcome", "summary"], "additionalProperties": true }, "closed": { "type": "object", "properties": { "reason": { "type": "string" }, "summary": { "type": "string" }, "at": { "type": "string" } }, "required": ["reason", "at"], "additionalProperties": true }, "description": { "type": "string" }, "labels": { "type": "array", "items": { "type": "string" } }, "paths": { "type": "array", "items": { "type": "string" } }, "progress": { "type": "object", "properties": { "done": { "type": "integer", "minimum": 0 }, "total": { "type": "integer", "minimum": 0 } }, "required": ["done", "total"], "additionalProperties": true }, "createdAt": { "type": "string" }, "updatedAt": { "type": "string" } }, "required": ["id", "title", "type", "priority", "status", "rev", "ready", "updatedAt"], "additionalProperties": true }, "libraryKind": { "enum": ["agents", "skills", "mcp", "hooks", "teams", "providers"] }, "libraryScope": { "enum": ["library", "project"] }, "librarySource": { "enum": ["builtin", "library", "project"] }, "entryName": { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[A-Za-z0-9_.-]+$", "not": { "enum": [".", ".."] } }, "libraryRevision": { "type": ["string", "null"] }, "libraryEntryRow": { "type": "object", "properties": { "name": { "type": "string" }, "source": { "$ref": "#/$defs/librarySource" }, "overrides": { "$ref": "#/$defs/librarySource" }, "description": { "type": "string" }, "usedBy": { "type": "array", "items": { "type": "string" } } }, "required": ["name", "source"], "additionalProperties": true }, "secretMarker": { "type": "object", "properties": { "$secret": { "const": "keep" } }, "required": ["$secret"], "additionalProperties": false, "description": "Stands for a stored secret value: read results carry it in place of the value; a save that sends it back keeps the stored value." }, "languageSetting": { "type": "object", "properties": { "language": { "type": ["string", "null"] }, "applies": { "type": "string" }, "default": { "type": "string" } }, "required": ["language", "applies", "default"], "additionalProperties": true }, "teamRow": { "type": "object", "properties": { "id": { "type": "string" }, "label": { "type": "string" }, "description": { "type": "string" }, "source": { "$ref": "#/$defs/librarySource" }, "overrides": { "$ref": "#/$defs/librarySource" }, "error": { "type": "string" } }, "required": ["id", "source"], "additionalProperties": true }, "promptImage": { "type": "object", "properties": { "type": { "const": "image" }, "mimeType": { "enum": ["image/png", "image/jpeg", "image/gif", "image/webp"] }, "data": { "type": "string", "minLength": 4, "maxLength": 6990508, "pattern": "^[A-Za-z0-9+/]+={0,2}$" }, "name": { "type": "string", "maxLength": 255 } }, "required": ["type", "mimeType", "data"], "additionalProperties": false } } };
|
|
38746
|
+
var METHODS_V1 = { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://alp.dev/schemas/methods-v1.json", "protocolVersion": 1, "errors": { "parseError": -32700, "invalidRequest": -32600, "methodNotFound": -32601, "invalidParams": -32602, "failed": 1e3, "notFound": 1001, "forbidden": 1003, "conflict": 1004, "unavailable": 1005, "protocol": 1006 }, "capabilities": { "attach.after": "session.attach accepts `after` and answers `cursor`/`reset`; envelopes kept in a tree timeline carry `cursor`.", "prompt.receipts": "session.prompt is answered at most once per (sessionId, clientMessageId) from a durable receipt; repeats answer the first outcome or outcome_unknown.", "extension.methods": "extension.register/unregister, and the daemon sends extension.invoke requests to the owning connection.", "team": "team.list: the teams a project can use, from the daemon host's built-ins, library and project.", "task": "task.list, task.create, task.update and task.gate.resolve on a project's .alp/tasks, written by the daemon under the task lock.", "library": "library.list/get/save/delete/duplicate/rename, library.skills.set and library.models on the daemon user's library and a project's .alp overrides; secrets are redacted on read.", "settings.language": "settings.language.get/set: the language the daemon user reads, in the daemon's ALP_HOME/settings.json.", "session.list.page": "session.list accepts `limit`, `cursor` and `query` and answers `nextCursor` while more sessions follow; without them it answers every session as before.", "question.watch": "question.watch: the connection is told of `question` and `question.resolved` in every tree (of a projectRoot when given), attached or not.", "question.watch.turns": "question.watch accepts `turns`: the connection is also told of `turn.ended` of every root in the trees it watches.", "workspace.files": "workspace.files.search: files under a directory whose relative path matches a query; read-only, names only, links never followed.", "workspace.files.read": "workspace.files.list and workspace.files.read: the entries of a directory and the content of a UTF-8 text file (at most 1 MiB) under a directory; read-only, links never followed, .git, .env* and key or credential files refused, .gitignore'd paths hidden unless asked.", "prompt.images": 'session.prompt takes inline image parts { type: "image", mimeType, data } (base64, no paths or URLs) within the image limits; a session snapshot says whether its runtime takes them in promptImages.', "library.project.write": "library save/delete/duplicate/rename accept policy project-data: only project-only pure-data agents and skills, no executable configuration, sidecars or library/built-in collisions." }, "methods": { "assignment.recall": { "x-alp": { "effect": "mutation", "scope": { "kind": "project", "param": "projectRoot", "required": false, "alternatives": ["assignmentId", "taskId"] }, "idempotent": false, "risks": ["model call"] }, "params": { "type": "object", "properties": { "assignmentId": { "type": "string" }, "taskId": { "type": "string" }, "projectRoot": { "type": "string" }, "question": { "type": "string" } }, "required": ["question"], "additionalProperties": true }, "result": { "type": "object", "properties": { "assignmentId": { "type": "string" }, "agent": { "type": "string" }, "taskId": { "type": "string" }, "finishedAt": { "type": "string" }, "answer": { "type": "string" } }, "required": ["assignmentId", "agent", "finishedAt", "answer"], "additionalProperties": true } }, "board.list": { "x-alp": { "effect": "query", "scope": { "kind": "project", "param": "projectRoot", "required": true }, "idempotent": true }, "params": { "type": "object", "properties": { "projectRoot": { "type": "string" } }, "required": ["projectRoot"], "additionalProperties": true }, "result": { "type": "object", "properties": { "pins": { "type": "array", "items": { "$ref": "#/$defs/pin" } } }, "required": ["pins"], "additionalProperties": true } }, "catalog.get": { "x-alp": { "effect": "query", "scope": { "kind": "host" }, "idempotent": true }, "params": { "type": "object", "properties": {}, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": { "models": { "type": "array", "items": { "type": "object" } }, "modes": { "type": "array", "items": { "type": "object" } }, "thinkingOptions": {}, "defaultModel": { "type": "string" } }, "required": ["models", "modes", "defaultModel"], "additionalProperties": true } }, "daemon.hello": { "x-alp": { "effect": "query", "scope": { "kind": "connection" }, "idempotent": true }, "params": { "type": "object", "properties": { "protocolVersion": { "const": 1 }, "client": { "type": "object", "properties": { "name": { "type": "string" }, "version": { "type": "string" } }, "required": ["name", "version"], "additionalProperties": true }, "capabilities": { "type": "array", "items": { "type": "string" } } }, "required": ["protocolVersion"], "additionalProperties": true }, "result": { "type": "object", "properties": { "protocolVersion": { "const": 1 }, "daemonVersion": { "type": "string" }, "pid": { "type": "integer" }, "hostname": { "type": "string" }, "capabilities": { "type": "array", "items": { "type": "string" } }, "promptImageLimits": { "type": "object", "description": "Optional effective inline image limits for this connection, when prompt.images is advertised. Missing on older peers. Byte limits count decoded bytes.", "properties": { "maxCount": { "type": "integer", "minimum": 0 }, "maxBytes": { "type": "integer", "minimum": 0 }, "maxTotalBytes": { "type": "integer", "minimum": 0 } }, "required": ["maxCount", "maxBytes", "maxTotalBytes"], "additionalProperties": false } }, "required": ["protocolVersion"], "additionalProperties": true } }, "daemon.pause": { "x-alp": { "effect": "mutation", "scope": { "kind": "host" }, "idempotent": true, "risks": ["admin"] }, "params": { "type": "object", "properties": { "runtime": { "enum": ["codex", "claude", "acp", "antigravity"] }, "now": { "type": "boolean" }, "reason": { "type": "string" } }, "required": [], "additionalProperties": true }, "result": { "$ref": "#/$defs/pauseState" } }, "daemon.pauses": { "x-alp": { "effect": "query", "scope": { "kind": "host" }, "idempotent": true }, "params": { "type": "object", "properties": {}, "required": [], "additionalProperties": true }, "result": { "$ref": "#/$defs/pauseState" } }, "daemon.resume": { "x-alp": { "effect": "mutation", "scope": { "kind": "host" }, "idempotent": true, "risks": ["admin"] }, "params": { "type": "object", "properties": { "runtime": { "enum": ["codex", "claude", "acp", "antigravity"] } }, "required": [], "additionalProperties": true }, "result": { "$ref": "#/$defs/pauseState" } }, "daemon.shutdown": { "x-alp": { "effect": "mutation", "scope": { "kind": "host" }, "idempotent": true, "risks": ["admin"] }, "params": { "type": "object", "properties": {}, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": {}, "required": [], "additionalProperties": true } }, "daemon.status": { "x-alp": { "effect": "query", "scope": { "kind": "host" }, "idempotent": true }, "params": { "type": "object", "properties": {}, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": { "pid": { "type": "integer" }, "startedAt": { "type": "string" }, "version": { "type": "string" }, "protocolVersion": { "type": "integer" }, "sessions": { "type": "integer" }, "questions": { "type": "integer" }, "previousExit": { "type": "object", "properties": { "kind": { "enum": ["clean", "crash"] }, "at": { "type": "string" } }, "required": ["kind"], "additionalProperties": true }, "persistence": { "type": "object" }, "health": { "$ref": "#/$defs/health" } }, "required": ["pid", "startedAt", "version", "protocolVersion", "sessions", "questions", "health"], "additionalProperties": true } }, "daemon.usage": { "x-alp": { "effect": "query", "scope": { "kind": "host" }, "idempotent": true, "risks": ["refresh asks every provider (spawns on the daemon host)"] }, "params": { "type": "object", "properties": { "refresh": { "type": "boolean" } }, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": { "providers": { "type": "array", "items": { "type": "object" } } }, "required": ["providers"], "additionalProperties": true } }, "extension.register": { "x-alp": { "effect": "mutation", "scope": { "kind": "connection" }, "idempotent": false, "risks": ["admin"] }, "params": { "type": "object", "properties": { "method": { "type": "string" } }, "required": ["method"], "additionalProperties": true }, "result": { "type": "object", "properties": { "registered": { "type": "string" } }, "required": ["registered"], "additionalProperties": true } }, "extension.unregister": { "x-alp": { "effect": "mutation", "scope": { "kind": "connection" }, "idempotent": false, "risks": ["admin"] }, "params": { "type": "object", "properties": { "method": { "type": "string" } }, "required": ["method"], "additionalProperties": true }, "result": { "type": "object", "properties": { "unregistered": { "type": "string" } }, "required": ["unregistered"], "additionalProperties": true } }, "library.delete": { "x-alp": { "effect": "mutation", "capability": "library", "scope": { "kind": "library-or-project", "param": "projectRoot", "selector": "scope" }, "idempotent": false }, "params": { "type": "object", "properties": { "kind": { "$ref": "#/$defs/libraryKind" }, "name": { "$ref": "#/$defs/entryName" }, "scope": { "$ref": "#/$defs/libraryScope" }, "projectRoot": { "$ref": "#/$defs/projectRoot" }, "revision": { "type": "string" }, "policy": { "enum": ["project-data"], "description": "Opt-in strict project-only non-executable mutation policy; requires library.project.write capability." } }, "required": ["kind", "name", "scope"], "additionalProperties": true }, "result": { "type": "object", "properties": { "removed": { "const": true }, "now": { "enum": ["builtin", "library"] } }, "required": ["removed"], "additionalProperties": true } }, "library.duplicate": { "x-alp": { "effect": "mutation", "capability": "library", "scope": { "kind": "library-or-project", "param": "projectRoot", "selector": "scope", "reads": ["builtin", "library"] }, "idempotent": false, "risks": ["exec-config when kind is mcp, hooks or providers"] }, "params": { "type": "object", "properties": { "kind": { "$ref": "#/$defs/libraryKind" }, "from": { "$ref": "#/$defs/entryName" }, "to": { "$ref": "#/$defs/entryName" }, "scope": { "$ref": "#/$defs/libraryScope" }, "projectRoot": { "$ref": "#/$defs/projectRoot" }, "policy": { "enum": ["project-data"], "description": "Opt-in strict project-only non-executable mutation policy; requires library.project.write capability." } }, "required": ["kind", "from", "to", "scope"], "additionalProperties": true }, "result": { "type": "object", "properties": { "revision": { "$ref": "#/$defs/libraryRevision" } }, "required": ["revision"], "additionalProperties": true } }, "library.get": { "x-alp": { "effect": "query", "capability": "library", "scope": { "kind": "project", "param": "projectRoot", "required": false, "reads": ["builtin", "library"] }, "idempotent": true, "redacts": ["mcp /server/env/*", "mcp /server/headers/*", "providers /provider/env/*"] }, "params": { "type": "object", "properties": { "kind": { "$ref": "#/$defs/libraryKind" }, "name": { "$ref": "#/$defs/entryName" }, "projectRoot": { "$ref": "#/$defs/projectRoot" }, "scope": { "$ref": "#/$defs/libraryScope" } }, "required": ["kind", "name"], "additionalProperties": true }, "result": { "type": "object", "properties": { "kind": { "$ref": "#/$defs/libraryKind" }, "name": { "type": "string" }, "source": { "$ref": "#/$defs/librarySource" }, "overrides": { "$ref": "#/$defs/librarySource" }, "content": { "type": "object" }, "revision": { "$ref": "#/$defs/libraryRevision" }, "usedBy": { "type": "array", "items": { "type": "string" } }, "librarySkills": { "type": "array", "items": { "type": "string" } }, "secrets": { "type": "array", "items": { "type": "string" } } }, "required": ["kind", "name", "source", "content", "revision", "usedBy"], "additionalProperties": true } }, "library.list": { "x-alp": { "effect": "query", "capability": "library", "scope": { "kind": "project", "param": "projectRoot", "required": false, "reads": ["builtin", "library"] }, "idempotent": true }, "params": { "type": "object", "properties": { "kind": { "$ref": "#/$defs/libraryKind" }, "projectRoot": { "$ref": "#/$defs/projectRoot" } }, "required": ["kind"], "additionalProperties": true }, "result": { "type": "object", "properties": { "projectRoot": { "type": ["string", "null"] }, "entries": { "type": "array", "items": { "$ref": "#/$defs/libraryEntryRow" } } }, "required": ["projectRoot", "entries"], "additionalProperties": true } }, "library.models": { "x-alp": { "effect": "query", "capability": "library", "scope": { "kind": "host" }, "idempotent": true }, "params": { "type": "object", "properties": {}, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": { "models": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "label": { "type": "string" }, "efforts": { "type": "array", "items": { "type": "string" } }, "effort": { "type": "string" } }, "required": ["id", "label", "efforts"], "additionalProperties": true } } }, "required": ["models"], "additionalProperties": true } }, "library.rename": { "x-alp": { "effect": "mutation", "capability": "library", "scope": { "kind": "library-or-project", "param": "projectRoot", "selector": "scope" }, "idempotent": false, "risks": ["exec-config when kind is mcp, hooks or providers"] }, "params": { "type": "object", "properties": { "kind": { "$ref": "#/$defs/libraryKind" }, "from": { "$ref": "#/$defs/entryName" }, "to": { "$ref": "#/$defs/entryName" }, "scope": { "$ref": "#/$defs/libraryScope" }, "projectRoot": { "$ref": "#/$defs/projectRoot" }, "policy": { "enum": ["project-data"], "description": "Opt-in strict project-only non-executable mutation policy; requires library.project.write capability." } }, "required": ["kind", "from", "to", "scope"], "additionalProperties": true }, "result": { "type": "object", "properties": { "revision": { "$ref": "#/$defs/libraryRevision" } }, "required": ["revision"], "additionalProperties": true } }, "library.save": { "x-alp": { "effect": "mutation", "capability": "library", "scope": { "kind": "library-or-project", "param": "projectRoot", "selector": "scope" }, "idempotent": "with revision (a repeat fails revision_conflict)", "risks": ["exec-config when kind is mcp, hooks or providers, or agents naming mcp/hooks/provider", "writes every known project's .alp/settings.json when kind is teams and sync is known-projects"] }, "params": { "type": "object", "properties": { "kind": { "$ref": "#/$defs/libraryKind" }, "name": { "$ref": "#/$defs/entryName" }, "scope": { "$ref": "#/$defs/libraryScope" }, "projectRoot": { "$ref": "#/$defs/projectRoot" }, "content": { "type": "object" }, "revision": { "$ref": "#/$defs/libraryRevision" }, "sync": { "enum": ["none", "known-projects"] }, "policy": { "enum": ["project-data"], "description": "Opt-in strict project-only non-executable mutation policy; requires library.project.write capability." } }, "required": ["kind", "name", "scope", "content"], "additionalProperties": true }, "result": { "type": "object", "properties": { "revision": { "$ref": "#/$defs/libraryRevision" }, "syncedProjects": { "type": "integer", "minimum": 0 }, "syncErrors": { "type": "array", "items": { "type": "string" } } }, "required": ["revision"], "additionalProperties": true } }, "library.skills.set": { "x-alp": { "effect": "mutation", "capability": "library", "scope": { "kind": "host" }, "writes": ["ALP_HOME/role-skills.json"], "idempotent": true }, "params": { "type": "object", "properties": { "agent": { "$ref": "#/$defs/entryName" }, "skills": { "type": "array", "items": { "$ref": "#/$defs/entryName" }, "maxItems": 100 } }, "required": ["agent", "skills"], "additionalProperties": true }, "result": { "type": "object", "properties": { "skills": { "type": "array", "items": { "type": "string" } } }, "required": ["skills"], "additionalProperties": true } }, "question.answer": { "x-alp": { "effect": "mutation", "scope": { "kind": "question", "param": "questionId", "prefix": true }, "idempotent": false }, "params": { "type": "object", "properties": { "questionId": { "type": "string" }, "text": { "type": "string" }, "dismiss": { "type": "boolean" }, "reason": { "type": "string" } }, "required": ["questionId"], "additionalProperties": true }, "result": { "type": "object", "properties": { "questionId": { "type": "string" } }, "required": ["questionId"], "additionalProperties": true } }, "question.list": { "x-alp": { "effect": "query", "scope": { "kind": "project", "param": "projectRoot", "required": false }, "idempotent": true }, "params": { "type": "object", "properties": { "projectRoot": { "type": "string" } }, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": { "questions": { "type": "array", "items": { "$ref": "#/$defs/question" } } }, "required": ["questions"], "additionalProperties": true } }, "question.watch": { "x-alp": { "effect": "query", "scope": { "kind": "project", "param": "projectRoot", "required": false }, "idempotent": true, "capability": "question.watch" }, "params": { "type": "object", "properties": { "projectRoot": { "type": "string" }, "turns": { "type": "boolean" } }, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": { "questions": { "type": "array", "items": { "$ref": "#/$defs/question" } } }, "required": ["questions"], "additionalProperties": true } }, "session.archive": { "x-alp": { "effect": "mutation", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" }, "archived": { "type": "boolean" } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": { "session": { "$ref": "#/$defs/sessionSummary" } }, "required": ["session"], "additionalProperties": true } }, "session.attach": { "x-alp": { "effect": "query", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" }, "replay": { "type": "boolean" }, "after": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": { "session": { "$ref": "#/$defs/sessionSummary" }, "cursor": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "reset": { "const": true } }, "required": ["session", "cursor"], "additionalProperties": true } }, "session.close": { "x-alp": { "effect": "mutation", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": false }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": {}, "required": [], "additionalProperties": true } }, "session.configure": { "x-alp": { "effect": "mutation", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" }, "mode": { "type": "string" } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": { "session": { "$ref": "#/$defs/session" } }, "required": ["session"], "additionalProperties": true } }, "session.create": { "x-alp": { "effect": "mutation", "scope": { "kind": "project", "param": "spec.cwd" }, "idempotent": false, "risks": ["exec: spec.env and stdio spec.mcpServers run on the daemon host", "spec.workdir/writeRoot/copy*/restore/keepThread are internal"] }, "params": { "type": "object", "properties": { "sessionId": { "type": "string", "pattern": "^[A-Za-z0-9_.:-]{1,128}$" }, "spec": { "$ref": "#/$defs/spec" }, "history": { "enum": ["skip", "replay"] }, "delegation": { "type": "boolean" }, "resume": { "type": "boolean" } }, "required": ["spec"], "additionalProperties": true }, "result": { "type": "object", "properties": { "session": { "$ref": "#/$defs/session" }, "attached": { "type": "boolean" }, "resumed": { "const": true }, "durable": { "type": "boolean" } }, "required": ["session", "attached"], "additionalProperties": true } }, "session.get": { "x-alp": { "effect": "query", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": { "session": { "$ref": "#/$defs/sessionSummary" } }, "required": ["session"], "additionalProperties": true } }, "session.interrupt": { "x-alp": { "effect": "mutation", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": {}, "required": [], "additionalProperties": true } }, "session.list": { "x-alp": { "effect": "query", "scope": { "kind": "project", "param": "projectRoot", "required": false }, "idempotent": true }, "params": { "type": "object", "properties": { "projectRoot": { "type": "string" }, "rootsOnly": { "type": "boolean" }, "includeClosed": { "type": "boolean" }, "includeArchived": { "type": "boolean" }, "limit": { "type": "integer", "minimum": 1, "maximum": 1e3 }, "cursor": { "type": "string", "minLength": 1, "maxLength": 1024 }, "query": { "type": "string", "maxLength": 200 }, "projectRoots": { "type": "array", "items": { "type": "string" }, "minItems": 1, "maxItems": 1024 } }, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": { "sessions": { "type": "array", "items": { "$ref": "#/$defs/sessionSummary" } }, "nextCursor": { "type": "string", "minLength": 1 } }, "required": ["sessions"], "additionalProperties": true } }, "session.log": { "x-alp": { "effect": "query", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": { "rootId": { "type": "string" }, "entries": { "type": "array", "items": { "type": "object" } } }, "required": ["rootId", "entries"], "additionalProperties": true } }, "session.message": { "x-alp": { "effect": "mutation", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": false }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" }, "text": { "type": "string" } }, "required": ["sessionId", "text"], "additionalProperties": true }, "result": { "type": "object", "properties": {}, "required": [], "additionalProperties": true } }, "session.preview": { "x-alp": { "effect": "query", "scope": { "kind": "project", "param": "spec.cwd" }, "idempotent": true }, "params": { "type": "object", "properties": { "spec": { "$ref": "#/$defs/spec" } }, "required": ["spec"], "additionalProperties": true }, "result": { "type": "object", "properties": { "preview": { "$ref": "#/$defs/preview" } }, "required": ["preview"], "additionalProperties": true } }, "session.prompt": { "x-alp": { "effect": "mutation", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": "with prompt.receipts" }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" }, "clientMessageId": { "type": "string" }, "delivery": { "enum": ["auto", "steer"] }, "content": { "type": "array", "items": { "anyOf": [{ "type": "object", "properties": { "type": { "const": "text" }, "text": { "type": "string" } }, "required": ["type"], "additionalProperties": true }, { "$ref": "#/$defs/promptImage" }, { "type": "object", "properties": { "type": { "type": "string", "not": { "enum": ["text", "image"] } } }, "required": ["type"], "additionalProperties": true }] } } }, "required": ["sessionId", "clientMessageId", "content"], "additionalProperties": true }, "result": { "type": "object", "properties": { "outcome": { "enum": ["accepted", "failed"] }, "error": { "$ref": "#/$defs/alpError" }, "duplicate": { "type": "boolean" }, "durable": { "type": "boolean" } }, "required": [], "additionalProperties": true } }, "session.release": { "x-alp": { "effect": "mutation", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": { "closed": { "type": "boolean" } }, "required": ["closed"], "additionalProperties": true } }, "session.rename": { "x-alp": { "effect": "mutation", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" }, "title": { "type": "string", "minLength": 1, "maxLength": 200, "pattern": "\\S" } }, "required": ["sessionId", "title"], "additionalProperties": true }, "result": { "type": "object", "properties": { "session": { "$ref": "#/$defs/sessionSummary" } }, "required": ["session"], "additionalProperties": true } }, "session.status": { "x-alp": { "effect": "query", "scope": { "kind": "session", "param": "sessionId" }, "idempotent": true }, "params": { "type": "object", "properties": { "sessionId": { "type": "string" } }, "required": ["sessionId"], "additionalProperties": true }, "result": { "type": "object", "properties": { "status": { "$ref": "#/$defs/treeStatus" } }, "required": ["status"], "additionalProperties": true } }, "settings.language.get": { "x-alp": { "effect": "query", "capability": "settings.language", "scope": { "kind": "host" }, "idempotent": true }, "params": { "type": "object", "properties": {}, "required": [], "additionalProperties": true }, "result": { "$ref": "#/$defs/languageSetting" } }, "settings.language.set": { "x-alp": { "effect": "mutation", "capability": "settings.language", "scope": { "kind": "host" }, "writes": ["ALP_HOME/settings.json"], "idempotent": true }, "params": { "type": "object", "properties": { "language": { "anyOf": [{ "type": "string", "minLength": 1, "maxLength": 40, "pattern": "\\S" }, { "type": "null" }] } }, "required": ["language"], "additionalProperties": true }, "result": { "$ref": "#/$defs/languageSetting" } }, "task.create": { "x-alp": { "effect": "mutation", "capability": "task", "scope": { "kind": "project", "param": "projectRoot", "required": true }, "writes": ["<projectRoot>/.alp/tasks"], "idempotent": "per requestId within one alpd process (10 minutes)" }, "params": { "type": "object", "properties": { "projectRoot": { "$ref": "#/$defs/projectRoot" }, "title": { "type": "string", "minLength": 1, "maxLength": 200, "pattern": "\\S" }, "priority": { "type": "integer", "minimum": 0, "maximum": 4 }, "requestId": { "type": "string", "minLength": 1, "maxLength": 128 } }, "required": ["projectRoot", "title"], "additionalProperties": true }, "result": { "type": "object", "properties": { "id": { "$ref": "#/$defs/taskId" }, "duplicate": { "const": true } }, "required": ["id"], "additionalProperties": true } }, "task.gate.resolve": { "x-alp": { "effect": "mutation", "capability": "task", "scope": { "kind": "project", "param": "projectRoot", "required": true }, "writes": ["<projectRoot>/.alp/tasks"], "idempotent": false }, "params": { "type": "object", "properties": { "projectRoot": { "$ref": "#/$defs/projectRoot" }, "id": { "$ref": "#/$defs/taskId" }, "gate": { "type": "string", "minLength": 1, "maxLength": 128 }, "note": { "type": "string", "maxLength": 2e3 }, "ifRev": { "type": "integer", "minimum": 0 } }, "required": ["projectRoot", "id", "gate"], "additionalProperties": true }, "result": { "type": "object", "properties": { "id": { "$ref": "#/$defs/taskId" }, "status": { "type": "string" }, "rev": { "type": "integer", "minimum": 0 } }, "required": ["id", "status", "rev"], "additionalProperties": true } }, "task.list": { "x-alp": { "effect": "query", "capability": "task", "scope": { "kind": "project", "param": "projectRoot", "required": true }, "idempotent": true }, "params": { "type": "object", "properties": { "projectRoot": { "$ref": "#/$defs/projectRoot" } }, "required": ["projectRoot"], "additionalProperties": true }, "result": { "type": "object", "properties": { "projectRoot": { "type": "string" }, "tasks": { "type": "array", "items": { "$ref": "#/$defs/taskRow" } }, "unreadable": { "type": "array", "items": { "type": "object", "properties": { "file": { "type": "string" }, "error": { "type": "string" } }, "required": ["file", "error"], "additionalProperties": true } }, "revision": { "type": "string" } }, "required": ["projectRoot", "tasks", "unreadable", "revision"], "additionalProperties": true } }, "task.update": { "x-alp": { "effect": "mutation", "capability": "task", "scope": { "kind": "project", "param": "projectRoot", "required": true }, "writes": ["<projectRoot>/.alp/tasks"], "idempotent": false }, "params": { "type": "object", "properties": { "projectRoot": { "$ref": "#/$defs/projectRoot" }, "id": { "$ref": "#/$defs/taskId" }, "action": { "enum": ["close", "reopen"] }, "note": { "type": "string", "maxLength": 2e3 }, "ifRev": { "type": "integer", "minimum": 0 } }, "required": ["projectRoot", "id", "action"], "additionalProperties": true }, "result": { "type": "object", "properties": { "id": { "$ref": "#/$defs/taskId" }, "status": { "type": "string" }, "rev": { "type": "integer", "minimum": 0 }, "landed": { "type": "string" } }, "required": ["id", "status", "rev"], "additionalProperties": true } }, "team.list": { "x-alp": { "effect": "query", "capability": "team", "scope": { "kind": "project", "param": "projectRoot", "required": false, "reads": ["builtin", "library"] }, "idempotent": true }, "params": { "type": "object", "properties": { "projectRoot": { "$ref": "#/$defs/projectRoot" } }, "required": [], "additionalProperties": true }, "result": { "type": "object", "properties": { "projectRoot": { "type": ["string", "null"] }, "teams": { "type": "array", "items": { "$ref": "#/$defs/teamRow" } } }, "required": ["projectRoot", "teams"], "additionalProperties": true } }, "workspace.files.search": { "x-alp": { "effect": "query", "scope": { "kind": "project", "param": "projectRoot", "required": true }, "idempotent": true, "capability": "workspace.files", "risks": ["lists file names of the directory (not contents)"] }, "params": { "type": "object", "properties": { "projectRoot": { "type": "string" }, "query": { "type": "string", "maxLength": 200 }, "limit": { "type": "integer", "minimum": 1, "maximum": 100 } }, "required": ["projectRoot"], "additionalProperties": true }, "result": { "type": "object", "properties": { "files": { "type": "array", "maxItems": 100, "items": { "type": "object", "properties": { "path": { "type": "string", "minLength": 1 } }, "required": ["path"], "additionalProperties": true } }, "truncated": { "type": "boolean" } }, "required": ["files"], "additionalProperties": true } }, "workspace.files.list": { "x-alp": { "effect": "query", "scope": { "kind": "project", "param": "projectRoot", "required": true }, "idempotent": true, "capability": "workspace.files.read", "risks": ["lists names, kinds, sizes and times of a directory's entries (not contents)"] }, "params": { "type": "object", "properties": { "projectRoot": { "type": "string" }, "path": { "type": "string", "minLength": 1, "maxLength": 4096 }, "includeIgnored": { "type": "boolean" } }, "required": ["projectRoot"], "additionalProperties": true }, "result": { "type": "object", "properties": { "entries": { "type": "array", "maxItems": 1e3, "items": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1 }, "path": { "type": "string", "minLength": 1 }, "kind": { "enum": ["file", "directory"] }, "size": { "type": "integer", "minimum": 0 }, "modifiedAt": { "type": "string" } }, "required": ["name", "path", "kind"], "additionalProperties": true } }, "truncated": { "type": "boolean" } }, "required": ["entries"], "additionalProperties": true } }, "workspace.files.read": { "x-alp": { "effect": "query", "scope": { "kind": "project", "param": "projectRoot", "required": true }, "idempotent": true, "capability": "workspace.files.read", "risks": ["returns the content of a text file under the directory"] }, "params": { "type": "object", "properties": { "projectRoot": { "type": "string" }, "path": { "type": "string", "minLength": 1, "maxLength": 4096 }, "includeIgnored": { "type": "boolean" } }, "required": ["projectRoot", "path"], "additionalProperties": true }, "result": { "type": "object", "properties": { "path": { "type": "string", "minLength": 1 }, "content": { "type": "string" }, "size": { "type": "integer", "minimum": 0, "maximum": 1048576 }, "modifiedAt": { "type": "string" } }, "required": ["path", "content", "size"], "additionalProperties": true } } }, "$defs": { "error": { "type": "object", "properties": { "code": { "type": "integer" }, "message": { "type": "string" }, "data": {} }, "required": ["code", "message"], "additionalProperties": true }, "session": { "type": "object", "properties": { "id": { "type": "string" }, "projectRoot": { "type": "string" }, "agent": { "type": "string" }, "runtime": { "enum": ["claude", "codex", "acp", "antigravity"] }, "model": { "type": "string" }, "mode": { "type": "string" }, "thinking": { "type": "string" }, "workflow": { "type": "object", "properties": { "mode": { "type": "string" }, "maxPeers": { "type": "integer" }, "supervisor": { "type": "boolean" } }, "required": ["mode", "maxPeers", "supervisor"], "additionalProperties": true }, "threadId": { "type": "string" }, "persistent": { "type": "boolean" }, "busy": { "type": "boolean" }, "parentId": { "type": "string" }, "toolCallId": { "type": "string" }, "activeTurnId": { "type": "string" }, "parked": { "type": "string" }, "instructionsSha": { "type": "string" }, "teamLabel": { "type": "string" }, "promptImages": { "type": "boolean" }, "usage": { "$ref": "#/$defs/usage" } }, "required": ["id", "projectRoot", "agent", "runtime", "model", "mode", "thinking", "workflow", "threadId", "persistent", "busy"], "additionalProperties": true }, "usage": { "type": "object", "properties": { "contextWindowUsedTokens": { "type": "integer", "minimum": 0 }, "contextWindowMaxTokens": { "type": "integer", "minimum": 1 }, "inputTokens": { "type": "integer", "minimum": 0 }, "cachedInputTokens": { "type": "integer", "minimum": 0 }, "outputTokens": { "type": "integer", "minimum": 0 }, "totalCostUsd": { "type": "number", "minimum": 0 } }, "additionalProperties": true }, "assignment": { "type": "object", "properties": { "id": { "type": "string" }, "agent": { "type": "string" }, "mode": { "type": "string" }, "status": { "type": "string" }, "startedAt": { "type": "string" }, "worktree": { "type": "object", "properties": { "branch": { "type": "string" }, "path": { "type": "string" } }, "required": ["branch", "path"], "additionalProperties": true } }, "required": ["id", "agent", "mode", "status", "startedAt"], "additionalProperties": true }, "mail": { "type": "object", "properties": { "id": { "type": "string" }, "kind": { "enum": ["question", "answer", "note", "steer", "result", "stalled", "board", "checkin"] }, "from": { "type": "string" }, "assignment": { "type": "string" }, "body": { "type": "string" }, "replyTo": { "type": "string" }, "result": { "type": "object" }, "redelivered": { "type": "boolean" } }, "required": ["id", "kind", "from", "assignment"], "additionalProperties": true }, "pin": { "type": "object", "properties": { "id": { "type": "string" }, "project": { "type": "string" }, "kind": { "enum": ["claim", "decision", "finding"] }, "body": { "type": "string" }, "paths": { "type": "array", "items": { "type": "string" } }, "agent": { "type": "string" }, "sessionId": { "type": "string" }, "rootId": { "type": "string" }, "at": { "type": "string" }, "released": { "type": "string" }, "task": { "type": "string" } }, "required": ["id", "project", "kind", "body", "agent", "sessionId", "rootId", "at"], "additionalProperties": true }, "question": { "type": "object", "properties": { "id": { "type": "string" }, "sessionId": { "type": "string" }, "rootId": { "type": "string" }, "agent": { "type": "string" }, "body": { "type": "string" }, "options": { "type": "array", "items": { "type": "string" } }, "askedAt": { "type": "string" } }, "required": ["id", "sessionId", "rootId", "agent", "body", "askedAt"], "additionalProperties": true }, "spec": { "type": "object", "properties": { "cwd": { "type": "string" }, "agent": { "type": "string" }, "model": { "type": "string" }, "mode": { "type": "string" }, "thinking": { "type": "string" }, "persist": { "type": "boolean" }, "restore": { "type": "object" } }, "required": ["cwd"], "additionalProperties": true }, "item": { "anyOf": [{ "$ref": "#/$defs/knownItem" }, { "$ref": "#/$defs/unknownItem" }] }, "event": { "anyOf": [{ "$ref": "#/$defs/knownEvent" }, { "$ref": "#/$defs/unknownEvent" }] }, "envelope": { "type": "object", "properties": { "sessionId": { "type": "string" }, "epoch": { "type": "string" }, "seq": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "ts": { "type": "string" }, "event": { "$ref": "#/$defs/event" }, "cursor": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 } }, "required": ["sessionId", "epoch", "seq", "ts", "event"], "additionalProperties": true }, "request": { "type": "object", "properties": { "jsonrpc": { "const": "2.0" }, "id": { "anyOf": [{ "type": "string", "maxLength": 128 }, { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }] }, "method": { "type": "string" }, "params": { "type": "object" } }, "required": ["jsonrpc", "method", "id"], "additionalProperties": true }, "response": { "oneOf": [{ "type": "object", "properties": { "jsonrpc": { "const": "2.0" }, "id": { "anyOf": [{ "type": "string", "maxLength": 128 }, { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" }] }, "result": {} }, "required": ["jsonrpc", "id", "result"], "additionalProperties": true }, { "type": "object", "properties": { "jsonrpc": { "const": "2.0" }, "id": { "anyOf": [{ "type": "string", "maxLength": 128 }, { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" }] }, "error": { "$ref": "#/$defs/error" } }, "required": ["jsonrpc", "id", "error"], "additionalProperties": true }] }, "notification": { "type": "object", "properties": { "jsonrpc": { "const": "2.0" }, "method": { "const": "event" }, "params": { "$ref": "#/$defs/envelope" } }, "required": ["jsonrpc", "method", "params"], "additionalProperties": true }, "knownEvent": { "oneOf": [{ "type": "object", "properties": { "type": { "const": "session.opened" }, "session": { "$ref": "#/$defs/session" }, "cwd": { "type": "string" }, "effective": { "type": "object", "properties": { "model": { "type": "string" }, "thinking": { "type": "string" } }, "required": ["model", "thinking"], "additionalProperties": true } }, "required": ["type", "session", "cwd", "effective"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "session.ready" } }, "required": ["type"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "session.updated" }, "session": { "$ref": "#/$defs/session" } }, "required": ["type", "session"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "session.closed" } }, "required": ["type"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "session.failed" }, "error": { "$ref": "#/$defs/alpError" } }, "required": ["type", "error"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "prompt.accepted" }, "clientMessageId": { "type": "string" }, "result": { "enum": ["turn", "steer"] }, "turnId": { "type": "string" } }, "required": ["type", "clientMessageId", "result", "turnId"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "prompt.failed" }, "clientMessageId": { "type": "string" }, "error": { "$ref": "#/$defs/alpError" } }, "required": ["type", "clientMessageId", "error"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "turn.started" }, "turnId": { "type": "string" }, "origin": { "enum": ["user", "wake", "assignment", "runtime"] } }, "required": ["type", "turnId", "origin"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "turn.ended" }, "turnId": { "type": "string" }, "state": { "enum": ["completed", "failed", "canceled"] }, "error": { "$ref": "#/$defs/alpError" } }, "required": ["type", "turnId", "state"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "item" }, "item": { "$ref": "#/$defs/item" } }, "required": ["type", "item"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "usage" }, "usage": { "$ref": "#/$defs/usage" } }, "required": ["type", "usage"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "mail" }, "mail": { "$ref": "#/$defs/mail" } }, "required": ["type", "mail"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "assignment" }, "assignment": { "$ref": "#/$defs/assignment" } }, "required": ["type", "assignment"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "question" }, "question": { "$ref": "#/$defs/question" } }, "required": ["type", "question"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "question.resolved" }, "questionId": { "type": "string" }, "outcome": { "enum": ["answered", "dismissed", "timeout", "canceled"] }, "answer": { "type": "string" } }, "required": ["type", "questionId", "outcome"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "pin" }, "pin": { "$ref": "#/$defs/pin" } }, "required": ["type", "pin"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "unpin" }, "pinId": { "type": "string" }, "reason": { "enum": ["unpinned", "session_ended"] } }, "required": ["type", "pinId", "reason"], "additionalProperties": true }] }, "unknownEvent": { "type": "object", "properties": { "type": { "type": "string", "not": { "enum": ["session.opened", "session.ready", "session.updated", "session.closed", "session.failed", "prompt.accepted", "prompt.failed", "turn.started", "turn.ended", "item", "usage", "mail", "assignment", "question", "question.resolved", "pin", "unpin"] } } }, "required": ["type"], "additionalProperties": true }, "knownItem": { "oneOf": [{ "type": "object", "properties": { "kind": { "const": "user_message" }, "id": { "type": "string" }, "text": { "type": "string" }, "clientMessageId": { "type": "string" }, "images": { "type": "integer", "minimum": 1 } }, "required": ["kind", "id", "text"], "additionalProperties": true }, { "type": "object", "properties": { "kind": { "const": "assistant_message" }, "id": { "type": "string" }, "text": { "type": "string" } }, "required": ["kind", "id", "text"], "additionalProperties": true }, { "type": "object", "properties": { "kind": { "const": "notice" }, "id": { "type": "string" }, "level": { "enum": ["info", "warning", "error"] }, "text": { "type": "string" } }, "required": ["kind", "id", "level", "text"], "additionalProperties": true }, { "type": "object", "properties": { "kind": { "const": "compaction" }, "id": { "type": "string" }, "status": { "enum": ["running", "completed", "failed"] }, "trigger": { "type": "string" }, "preTokens": { "type": "integer" }, "postTokens": { "type": "integer" } }, "required": ["kind", "id", "status"], "additionalProperties": true }, { "type": "object", "properties": { "kind": { "const": "todo" }, "id": { "type": "string" }, "items": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "text": { "type": "string" }, "status": { "enum": ["pending", "in_progress", "completed"] } }, "required": ["id", "text", "status"], "additionalProperties": true } } }, "required": ["kind", "id", "items"], "additionalProperties": true }, { "type": "object", "properties": { "kind": { "const": "tool_call" }, "id": { "type": "string" }, "callId": { "type": "string" }, "name": { "type": "string" }, "status": { "enum": ["running", "completed", "failed"] }, "error": { "type": "string" }, "detail": { "$ref": "#/$defs/toolCallDetail" } }, "required": ["kind", "id", "callId", "name", "status", "detail"], "additionalProperties": true }] }, "unknownItem": { "type": "object", "properties": { "kind": { "type": "string", "not": { "enum": ["user_message", "assistant_message", "notice", "compaction", "todo", "tool_call"] } }, "id": { "type": "string" } }, "required": ["kind", "id"], "additionalProperties": true }, "toolCallDetail": { "oneOf": [{ "type": "object", "properties": { "type": { "const": "shell" }, "command": { "type": "string" }, "cwd": { "type": "string" }, "output": { "type": "string" }, "exitCode": { "type": ["integer", "null"] } }, "required": ["type", "command", "output"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "read" }, "filePath": { "type": "string" }, "content": { "type": "string" }, "offset": { "type": "integer" }, "limit": { "type": "integer" } }, "required": ["type", "filePath"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "edit" }, "filePath": { "type": "string" }, "oldString": { "type": "string" }, "newString": { "type": "string" }, "unifiedDiff": { "type": "string" } }, "required": ["type", "filePath"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "write" }, "filePath": { "type": "string" }, "content": { "type": "string" } }, "required": ["type", "filePath"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "search" }, "query": { "type": "string" }, "toolName": { "enum": ["search", "grep", "glob", "web_search"] }, "content": { "type": "string" }, "filePaths": { "type": "array", "items": { "type": "string" } }, "webResults": { "type": "array", "items": { "type": "object", "properties": { "title": { "type": "string" }, "url": { "type": "string" } }, "required": ["title", "url"], "additionalProperties": true } }, "numFiles": { "type": "integer" }, "numMatches": { "type": "integer" }, "truncated": { "type": "boolean" } }, "required": ["type", "query"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "fetch" }, "url": { "type": "string" }, "prompt": { "type": "string" }, "result": { "type": "string" }, "code": { "type": "integer" } }, "required": ["type", "url"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "sub_agent" }, "subAgentType": { "type": "string" }, "description": { "type": "string" }, "log": { "type": "string" } }, "required": ["type"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "plan" }, "text": { "type": "string" } }, "required": ["type", "text"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "plain_text" }, "label": { "type": "string" }, "text": { "type": "string" } }, "required": ["type"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "const": "unknown" }, "input": {}, "output": {} }, "required": ["type", "input", "output"], "additionalProperties": true }, { "type": "object", "properties": { "type": { "type": "string", "not": { "enum": ["shell", "read", "edit", "write", "search", "fetch", "sub_agent", "plan", "plain_text", "unknown"] } } }, "required": ["type"], "additionalProperties": true }] }, "alpError": { "type": "object", "properties": { "message": { "type": "string" }, "code": { "type": "string" } }, "required": ["message"], "additionalProperties": true }, "sessionSummary": { "type": "object", "properties": { "id": { "type": "string" }, "projectRoot": { "type": "string" }, "agent": { "type": "string" }, "runtime": { "enum": ["claude", "codex", "acp", "antigravity"] }, "model": { "type": "string" }, "mode": { "type": "string" }, "thinking": { "type": "string" }, "workflow": { "type": "object", "properties": { "mode": { "type": "string" }, "maxPeers": { "type": "integer" }, "supervisor": { "type": "boolean" } }, "required": ["mode", "maxPeers", "supervisor"], "additionalProperties": true }, "threadId": { "type": "string" }, "persistent": { "type": "boolean" }, "busy": { "type": "boolean" }, "parentId": { "type": "string" }, "toolCallId": { "type": "string" }, "activeTurnId": { "type": "string" }, "parked": { "type": "string" }, "instructionsSha": { "type": "string" }, "teamLabel": { "type": "string" }, "status": { "enum": ["initializing", "idle", "running", "error", "closed"] }, "title": { "type": "string" }, "archived": { "type": "boolean" }, "lastError": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" } }, "required": ["message"], "additionalProperties": true }, "createdAt": { "type": "string" }, "updatedAt": { "type": "string" } }, "required": ["id", "projectRoot", "agent", "runtime", "model", "mode", "thinking", "workflow", "threadId", "persistent", "busy", "status"], "additionalProperties": true }, "pause": { "type": "object", "properties": { "since": { "type": "string" }, "by": { "type": "string" }, "reason": { "type": "string" }, "resetsAt": { "type": "string" }, "checkedAt": { "type": "string" } }, "required": ["since", "by", "reason"], "additionalProperties": true }, "pauseState": { "type": "object", "properties": { "all": { "$ref": "#/$defs/pause" }, "runtimes": { "type": "object", "additionalProperties": { "$ref": "#/$defs/pause" } }, "parked": { "type": "array", "items": { "type": "object", "properties": { "assignmentId": { "type": "string" }, "agent": { "type": "string" }, "runtime": { "type": "string" }, "rootId": { "type": "string" }, "reason": { "type": "string" }, "since": { "type": "string" } }, "required": ["assignmentId", "agent", "runtime", "rootId", "reason", "since"], "additionalProperties": true } } }, "required": ["runtimes", "parked"], "additionalProperties": true }, "preview": { "type": "object", "properties": { "teams": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "label": { "type": "string" }, "description": { "type": "string" } }, "required": ["id", "label"], "additionalProperties": true } }, "team": { "type": "string" }, "teamLabel": { "type": "string" }, "agent": { "type": "string" }, "runtime": { "enum": ["claude", "codex", "acp", "antigravity"] }, "model": { "type": "string" }, "mode": { "type": "string" }, "thinking": { "type": "string" } }, "required": ["teams", "team", "agent", "runtime", "model", "mode", "thinking"], "additionalProperties": true }, "treeStatus": { "type": "object", "properties": { "rootId": { "type": "string" }, "sessions": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "projectRoot": { "type": "string" }, "agent": { "type": "string" }, "runtime": { "enum": ["claude", "codex", "acp", "antigravity"] }, "model": { "type": "string" }, "mode": { "type": "string" }, "thinking": { "type": "string" }, "workflow": { "type": "object", "properties": { "mode": { "type": "string" }, "maxPeers": { "type": "integer" }, "supervisor": { "type": "boolean" } }, "required": ["mode", "maxPeers", "supervisor"], "additionalProperties": true }, "threadId": { "type": "string" }, "persistent": { "type": "boolean" }, "busy": { "type": "boolean" }, "parentId": { "type": "string" }, "toolCallId": { "type": "string" }, "activeTurnId": { "type": "string" }, "parked": { "type": "string" }, "instructionsSha": { "type": "string" }, "teamLabel": { "type": "string" }, "state": { "enum": ["running", "waiting", "waiting_parent", "waiting_user", "idle"] }, "idleMs": { "type": "number" }, "workdir": { "type": "string" }, "unreadMail": { "type": "integer", "minimum": 0 } }, "required": ["id", "projectRoot", "agent", "runtime", "model", "mode", "thinking", "workflow", "threadId", "persistent", "busy", "state", "idleMs", "workdir", "unreadMail"], "additionalProperties": true } }, "assignments": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "requester": { "type": "string" }, "isolation": { "enum": ["shared", "worktree"] }, "idleMs": { "type": "number" } }, "required": ["id", "requester", "isolation", "idleMs"], "additionalProperties": true } }, "questions": { "type": "array", "items": { "$ref": "#/$defs/question" } }, "worktrees": { "type": "array", "items": { "type": "object", "properties": { "assignmentId": { "type": "string" }, "requester": { "type": "string" }, "agent": { "type": "string" }, "branch": { "type": "string" }, "files": { "type": "array", "items": { "type": "string" } }, "stat": { "type": "string" } }, "required": ["assignmentId", "requester", "agent", "branch", "files", "stat"], "additionalProperties": true } }, "leases": { "type": "array", "items": { "type": "object", "properties": { "checkout": { "type": "string" }, "assignmentId": { "type": "string" }, "agent": { "type": "string" } }, "required": ["checkout", "assignmentId", "agent"], "additionalProperties": true } }, "claims": { "type": "array", "items": { "$ref": "#/$defs/pin" } } }, "required": ["rootId", "sessions", "assignments", "questions", "worktrees", "leases", "claims"], "additionalProperties": true }, "health": { "type": "object", "properties": { "live": { "type": "boolean" }, "ready": { "type": "boolean" }, "checks": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "ok": { "type": "boolean" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": ["id", "ok"], "additionalProperties": true } } }, "required": ["live", "ready", "checks"], "additionalProperties": true }, "projectRoot": { "type": "string", "minLength": 1, "maxLength": 4096, "description": "An absolute, normalized path on the daemon host to a directory holding .alp/. Compared as is: the daemon never walks up to a parent." }, "taskId": { "type": "string", "pattern": "^t-[0-9a-f]{4,12}(?:\\.\\d+)*$" }, "taskRow": { "type": "object", "properties": { "id": { "$ref": "#/$defs/taskId" }, "title": { "type": "string" }, "type": { "type": "string" }, "priority": { "type": "integer" }, "status": { "enum": ["open", "in_progress", "review", "closed"] }, "rev": { "type": "integer", "minimum": 0 }, "parent": { "type": "string" }, "assignee": { "type": "string" }, "ready": { "type": "boolean" }, "blockedBy": { "type": "array", "items": { "type": "string" } }, "waits": { "type": "array", "items": { "type": "string" } }, "approvals": { "type": "array", "items": { "type": "object", "properties": { "gate": { "type": "string" }, "note": { "type": "string" } }, "required": ["gate", "note"], "additionalProperties": true } }, "handoff": { "type": "object", "properties": { "outcome": { "type": "string" }, "summary": { "type": "string" }, "agent": { "type": "string" } }, "required": ["outcome", "summary"], "additionalProperties": true }, "closed": { "type": "object", "properties": { "reason": { "type": "string" }, "summary": { "type": "string" }, "at": { "type": "string" } }, "required": ["reason", "at"], "additionalProperties": true }, "description": { "type": "string" }, "labels": { "type": "array", "items": { "type": "string" } }, "paths": { "type": "array", "items": { "type": "string" } }, "progress": { "type": "object", "properties": { "done": { "type": "integer", "minimum": 0 }, "total": { "type": "integer", "minimum": 0 } }, "required": ["done", "total"], "additionalProperties": true }, "createdAt": { "type": "string" }, "updatedAt": { "type": "string" } }, "required": ["id", "title", "type", "priority", "status", "rev", "ready", "updatedAt"], "additionalProperties": true }, "libraryKind": { "enum": ["agents", "skills", "mcp", "hooks", "teams", "providers"] }, "libraryScope": { "enum": ["library", "project"] }, "librarySource": { "enum": ["builtin", "library", "project"] }, "entryName": { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[A-Za-z0-9_.-]+$", "not": { "enum": [".", ".."] } }, "libraryRevision": { "type": ["string", "null"] }, "libraryEntryRow": { "type": "object", "properties": { "name": { "type": "string" }, "source": { "$ref": "#/$defs/librarySource" }, "overrides": { "$ref": "#/$defs/librarySource" }, "description": { "type": "string" }, "usedBy": { "type": "array", "items": { "type": "string" } } }, "required": ["name", "source"], "additionalProperties": true }, "secretMarker": { "type": "object", "properties": { "$secret": { "const": "keep" } }, "required": ["$secret"], "additionalProperties": false, "description": "Stands for a stored secret value: read results carry it in place of the value; a save that sends it back keeps the stored value." }, "languageSetting": { "type": "object", "properties": { "language": { "type": ["string", "null"] }, "applies": { "type": "string" }, "default": { "type": "string" } }, "required": ["language", "applies", "default"], "additionalProperties": true }, "teamRow": { "type": "object", "properties": { "id": { "type": "string" }, "label": { "type": "string" }, "description": { "type": "string" }, "source": { "$ref": "#/$defs/librarySource" }, "overrides": { "$ref": "#/$defs/librarySource" }, "error": { "type": "string" } }, "required": ["id", "source"], "additionalProperties": true }, "promptImage": { "type": "object", "properties": { "type": { "const": "image" }, "mimeType": { "enum": ["image/png", "image/jpeg", "image/gif", "image/webp"] }, "data": { "type": "string", "minLength": 4, "maxLength": 6990508, "pattern": "^[A-Za-z0-9+/]+={0,2}$" }, "name": { "type": "string", "maxLength": 255 } }, "required": ["type", "mimeType", "data"], "additionalProperties": false } } };
|
|
38494
38747
|
|
|
38495
38748
|
// packages/protocol/src/validate.js
|
|
38496
38749
|
var MAX_ISSUES = 8;
|
|
@@ -38663,7 +38916,7 @@ var RpcError = class extends Error {
|
|
|
38663
38916
|
|
|
38664
38917
|
// packages/daemon/src/operations.ts
|
|
38665
38918
|
import { createHash as createHash10, createHmac, randomBytes as randomBytes6 } from "node:crypto";
|
|
38666
|
-
import { lstat as
|
|
38919
|
+
import { lstat as lstat6, stat as stat8 } from "node:fs/promises";
|
|
38667
38920
|
import path32 from "node:path";
|
|
38668
38921
|
|
|
38669
38922
|
// packages/core/src/task-rows.js
|
|
@@ -38709,8 +38962,8 @@ async function taskRows(projectRoot) {
|
|
|
38709
38962
|
}
|
|
38710
38963
|
|
|
38711
38964
|
// packages/core/src/library-edit.js
|
|
38712
|
-
import { mkdir as mkdir13, rename as
|
|
38713
|
-
import { createHash as createHash9, randomUUID as
|
|
38965
|
+
import { lstat as lstat5, mkdir as mkdir13, readdir as readdir9, rename as rename7, rm as rm5, stat as stat7, writeFile as writeFile15 } from "node:fs/promises";
|
|
38966
|
+
import { createHash as createHash9, randomUUID as randomUUID13 } from "node:crypto";
|
|
38714
38967
|
import path31 from "node:path";
|
|
38715
38968
|
var EDIT_KINDS = ["agents", "skills", "mcp", "hooks", "teams", "providers"];
|
|
38716
38969
|
var validName3 = (name2) => typeof name2 === "string" && /^[\w.-]+$/.test(name2) && name2 !== "." && name2 !== "..";
|
|
@@ -38742,6 +38995,42 @@ function base(scope, { root, library }, kind) {
|
|
|
38742
38995
|
return fail5("INVALID_SCOPE", `Scope must be library or project, not '${scope}'`);
|
|
38743
38996
|
}
|
|
38744
38997
|
var revisionOf = (content) => content === void 0 ? null : createHash9("sha256").update(JSON.stringify(content)).digest("hex").slice(0, 16);
|
|
38998
|
+
function assertProjectDataContent(kind, content) {
|
|
38999
|
+
const deny = () => fail5("UNSAFE_LIBRARY_WRITE", "Project-data writes allow only instruction entries and non-executable agent configuration");
|
|
39000
|
+
if (!object8(content) || !["agents", "skills"].includes(kind)) deny();
|
|
39001
|
+
const keys2 = kind === "skills" ? ["body"] : ["instructions", "config"];
|
|
39002
|
+
if (Object.keys(content).some((key2) => !keys2.includes(key2))) deny();
|
|
39003
|
+
const text2 = kind === "skills" ? content.body : content.instructions;
|
|
39004
|
+
if (typeof text2 !== "string" || !text2.trim()) deny();
|
|
39005
|
+
if (content.config !== void 0) {
|
|
39006
|
+
const config2 = content.config;
|
|
39007
|
+
if (!object8(config2)) deny();
|
|
39008
|
+
const allowed = ["description", "model", "thinking", "context", "autocompact"];
|
|
39009
|
+
if (Object.keys(config2).some((key2) => !allowed.includes(key2))) deny();
|
|
39010
|
+
for (const key2 of ["description", "thinking"]) if (config2[key2] !== void 0 && typeof config2[key2] !== "string") deny();
|
|
39011
|
+
if (config2.model !== void 0 && (typeof config2.model !== "string" || !/^(codex|claude):[^\s/\\:]{1,240}$/.test(config2.model))) deny();
|
|
39012
|
+
for (const key2 of ["context", "autocompact"]) if (config2[key2] !== void 0 && !(typeof config2[key2] === "string" || typeof config2[key2] === "number" && Number.isFinite(config2[key2]))) deny();
|
|
39013
|
+
}
|
|
39014
|
+
}
|
|
39015
|
+
async function projectDataEntry(kind, name2, { scope, root, library, templates }) {
|
|
39016
|
+
if (scope !== "project" || !root || !["agents", "skills"].includes(kind)) fail5("UNSAFE_LIBRARY_WRITE", "Project-data writes require project scope and agents or skills");
|
|
39017
|
+
nameOf(name2);
|
|
39018
|
+
const directory = base(scope, { root, library }, kind);
|
|
39019
|
+
await safeEntryPaths(directory, kind, name2);
|
|
39020
|
+
const found = await layers(kind, name2, { root, library, templates });
|
|
39021
|
+
if (found.library !== void 0 || found.builtin !== void 0) fail5("UNSAFE_LIBRARY_WRITE", "Project-data writes cannot shadow or reveal a library or built-in entry");
|
|
39022
|
+
if (found.project !== void 0) assertProjectDataContent(kind, found.project);
|
|
39023
|
+
const owned = path31.join(directory, kind, name2);
|
|
39024
|
+
const files = await readdir9(owned).catch((error51) => {
|
|
39025
|
+
if (error51.code === "ENOENT") return [];
|
|
39026
|
+
throw error51;
|
|
39027
|
+
});
|
|
39028
|
+
const allowed = new Set(FILES[kind](name2).map(([file3]) => path31.basename(file3)));
|
|
39029
|
+
for (const file3 of files) {
|
|
39030
|
+
const info = await lstat5(path31.join(owned, file3));
|
|
39031
|
+
if (!allowed.has(file3) || !info.isFile() || info.isSymbolicLink() || info.nlink !== 1) fail5("UNSAFE_LIBRARY_WRITE", "Project-data entries cannot contain sidecars, links or executable assets");
|
|
39032
|
+
}
|
|
39033
|
+
}
|
|
38745
39034
|
async function safeEntryPaths(directory, kind, name2) {
|
|
38746
39035
|
for (const [file3] of FILES[kind](name2)) {
|
|
38747
39036
|
await assertNoSymlinks(directory, path31.join(kind, file3));
|
|
@@ -38934,14 +39223,18 @@ async function check3(kind, name2, content, { scope, root, library, templates })
|
|
|
38934
39223
|
}
|
|
38935
39224
|
async function writeAtomic2(file3, text2) {
|
|
38936
39225
|
await mkdir13(path31.dirname(file3), { recursive: true });
|
|
38937
|
-
const temporary = `${file3}.${
|
|
38938
|
-
await
|
|
39226
|
+
const temporary = `${file3}.${randomUUID13().slice(0, 8)}.tmp`;
|
|
39227
|
+
await writeFile15(temporary, text2);
|
|
38939
39228
|
await moveFile(temporary, file3);
|
|
38940
39229
|
}
|
|
38941
39230
|
var exists2 = (target) => stat7(target).then(() => true, () => false);
|
|
38942
|
-
async function saveEntry(kind, name2, content, { scope, root, library, templates, revision } = {}) {
|
|
39231
|
+
async function saveEntry(kind, name2, content, { scope, root, library, templates, revision, policy } = {}) {
|
|
38943
39232
|
kindOf(kind);
|
|
38944
39233
|
nameOf(name2);
|
|
39234
|
+
if (policy === "project-data") {
|
|
39235
|
+
assertProjectDataContent(kind, content);
|
|
39236
|
+
await projectDataEntry(kind, name2, { scope, root, library, templates });
|
|
39237
|
+
}
|
|
38945
39238
|
const directory = base(scope, { root, library }, kind);
|
|
38946
39239
|
await safeEntryPaths(directory, kind, name2);
|
|
38947
39240
|
await check3(kind, name2, content, { scope, root, library, templates });
|
|
@@ -38963,9 +39256,10 @@ async function saveEntry(kind, name2, content, { scope, root, library, templates
|
|
|
38963
39256
|
}
|
|
38964
39257
|
return { revision: revisionOf(await readLayer(kind, name2, directory)) };
|
|
38965
39258
|
}
|
|
38966
|
-
async function deleteEntry(kind, name2, { scope, root, library, templates, revision } = {}) {
|
|
39259
|
+
async function deleteEntry(kind, name2, { scope, root, library, templates, revision, policy } = {}) {
|
|
38967
39260
|
kindOf(kind);
|
|
38968
39261
|
nameOf(name2);
|
|
39262
|
+
if (policy === "project-data") await projectDataEntry(kind, name2, { scope, root, library, templates });
|
|
38969
39263
|
const directory = base(scope, { root, library }, kind);
|
|
38970
39264
|
const current = await readLayer(kind, name2, directory);
|
|
38971
39265
|
if (current === void 0) fail5("NOT_FOUND", `No ${kind} entry '${name2}' in the ${scope}`);
|
|
@@ -38981,14 +39275,25 @@ async function deleteEntry(kind, name2, { scope, root, library, templates, revis
|
|
|
38981
39275
|
const now2 = scope === "project" && found.library !== void 0 ? "library" : "builtin";
|
|
38982
39276
|
return { removed: true, ...fallback !== void 0 ? { now: now2 } : {} };
|
|
38983
39277
|
}
|
|
38984
|
-
async function duplicateEntry(kind, from, to, { scope, root, library, templates } = {}) {
|
|
39278
|
+
async function duplicateEntry(kind, from, to, { scope, root, library, templates, policy } = {}) {
|
|
39279
|
+
kindOf(kind);
|
|
39280
|
+
nameOf(from);
|
|
39281
|
+
nameOf(to);
|
|
39282
|
+
if (policy === "project-data") {
|
|
39283
|
+
await projectDataEntry(kind, from, { scope, root, library, templates });
|
|
39284
|
+
await projectDataEntry(kind, to, { scope, root, library, templates });
|
|
39285
|
+
}
|
|
38985
39286
|
const { content } = await getEntry(kind, from, { root, library, templates });
|
|
38986
|
-
return saveEntry(kind, to, structuredClone(content), { scope, root, library, templates, revision: null });
|
|
39287
|
+
return saveEntry(kind, to, structuredClone(content), { scope, root, library, templates, revision: null, policy });
|
|
38987
39288
|
}
|
|
38988
|
-
async function renameEntry(kind, from, to, { scope, root, library, templates } = {}) {
|
|
39289
|
+
async function renameEntry(kind, from, to, { scope, root, library, templates, policy } = {}) {
|
|
38989
39290
|
kindOf(kind);
|
|
38990
39291
|
nameOf(from);
|
|
38991
39292
|
nameOf(to);
|
|
39293
|
+
if (policy === "project-data") {
|
|
39294
|
+
await projectDataEntry(kind, from, { scope, root, library, templates });
|
|
39295
|
+
await projectDataEntry(kind, to, { scope, root, library, templates });
|
|
39296
|
+
}
|
|
38992
39297
|
const directory = base(scope, { root, library }, kind);
|
|
38993
39298
|
const content = await readLayer(kind, from, directory);
|
|
38994
39299
|
if (content === void 0) fail5("NOT_FOUND", `No ${kind} entry '${from}' in the ${scope}`);
|
|
@@ -38998,9 +39303,9 @@ async function renameEntry(kind, from, to, { scope, root, library, templates } =
|
|
|
38998
39303
|
await check3(kind, to, content, { scope, root, library, templates });
|
|
38999
39304
|
if (ownDirectory(kind)) {
|
|
39000
39305
|
if (await exists2(path31.join(directory, kind, to))) fail5("REVISION_CONFLICT", `${path31.join(directory, kind, to)} already exists`);
|
|
39001
|
-
await
|
|
39306
|
+
await rename7(path31.join(directory, kind, from), path31.join(directory, kind, to));
|
|
39002
39307
|
} else {
|
|
39003
|
-
await
|
|
39308
|
+
await rename7(path31.join(directory, kind, `${from}.json`), path31.join(directory, kind, `${to}.json`));
|
|
39004
39309
|
}
|
|
39005
39310
|
return { revision: revisionOf(await readLayer(kind, to, directory)) };
|
|
39006
39311
|
}
|
|
@@ -39019,6 +39324,8 @@ function fromCore(error51, context = {}) {
|
|
|
39019
39324
|
if (error51 instanceof RpcError || typeof error51?.code !== "string" || error51?.name !== "AlpError") return error51;
|
|
39020
39325
|
const message = error51.message;
|
|
39021
39326
|
switch (error51.code) {
|
|
39327
|
+
case "UNSAFE_LIBRARY_WRITE":
|
|
39328
|
+
return failure(ERROR.forbidden, "library_policy", message);
|
|
39022
39329
|
case "UNSAFE_PATH":
|
|
39023
39330
|
return failure(ERROR.forbidden, "unsafe_path", message);
|
|
39024
39331
|
case "TASK_NOT_FOUND":
|
|
@@ -39108,7 +39415,7 @@ function createOperations({ home: home2, templates, knownProjects = () => [] } =
|
|
|
39108
39415
|
async function project(value) {
|
|
39109
39416
|
if (!path32.isAbsolute(value) || path32.resolve(value) !== value) throw failure(RPC_ERROR.invalidParams, "invalid_path", "projectRoot must be an absolute, normalized path");
|
|
39110
39417
|
if (!(await stat8(value).catch(() => void 0))?.isDirectory()) throw failure(ERROR.notFound, "not_alp_project", `${value} is not a directory`);
|
|
39111
|
-
const alp = await
|
|
39418
|
+
const alp = await lstat6(path32.join(value, ".alp")).catch(() => void 0);
|
|
39112
39419
|
if (alp?.isSymbolicLink()) throw failure(ERROR.forbidden, "unsafe_path", `${path32.join(value, ".alp")} is a symlink`);
|
|
39113
39420
|
if (!alp?.isDirectory()) throw failure(ERROR.notFound, "not_alp_project", `${value} is not an ALP project; run alp init there`);
|
|
39114
39421
|
return value;
|
|
@@ -39194,7 +39501,8 @@ function createOperations({ home: home2, templates, knownProjects = () => [] } =
|
|
|
39194
39501
|
const { content, secrets } = redact2(kind, entry.content);
|
|
39195
39502
|
return { ...entry, content, revision: toWire(entry.revision), ...secrets.length ? { secrets } : {} };
|
|
39196
39503
|
}),
|
|
39197
|
-
"library.save": guard(({ kind, name: name2, scope, projectRoot, content, revision, sync = "none" }) => serial(async () => {
|
|
39504
|
+
"library.save": guard(({ kind, name: name2, scope, projectRoot, content, revision, policy, sync = "none" }) => serial(async () => {
|
|
39505
|
+
if (policy === "project-data" && sync !== "none") throw failure(ERROR.forbidden, "library_policy", "Project-data writes cannot synchronize projects");
|
|
39198
39506
|
const options2 = await where(projectRoot, scope);
|
|
39199
39507
|
const current = await layer(kind, name2, options2, scope);
|
|
39200
39508
|
if (revision !== void 0 && revision !== toWire(current?.revision ?? null)) {
|
|
@@ -39202,7 +39510,7 @@ function createOperations({ home: home2, templates, knownProjects = () => [] } =
|
|
|
39202
39510
|
}
|
|
39203
39511
|
const previous = current?.content ?? (await layer(kind, name2, options2))?.content;
|
|
39204
39512
|
const next = unredact(kind, content, previous);
|
|
39205
|
-
const saved = await saveEntry(kind, name2, next, { ...options2, scope, ...revision !== void 0 ? { revision: current?.revision ?? null } : {} });
|
|
39513
|
+
const saved = await saveEntry(kind, name2, next, { ...options2, scope, policy, ...revision !== void 0 ? { revision: current?.revision ?? null } : {} });
|
|
39206
39514
|
if (kind !== "teams" || sync !== "known-projects") return { revision: toWire(saved.revision) };
|
|
39207
39515
|
const roots = new Set(options2.root ? [options2.root] : []);
|
|
39208
39516
|
if (scope === "library") {
|
|
@@ -39219,25 +39527,25 @@ function createOperations({ home: home2, templates, knownProjects = () => [] } =
|
|
|
39219
39527
|
}
|
|
39220
39528
|
return { revision: toWire(saved.revision), syncedProjects, syncErrors };
|
|
39221
39529
|
}), { notFound: "invalid_entry" }),
|
|
39222
|
-
"library.delete": guard(({ kind, name: name2, scope, projectRoot, revision }) => serial(async () => {
|
|
39530
|
+
"library.delete": guard(({ kind, name: name2, scope, projectRoot, revision, policy }) => serial(async () => {
|
|
39223
39531
|
const options2 = await where(projectRoot, scope);
|
|
39224
39532
|
const current = await layer(kind, name2, options2, scope);
|
|
39225
39533
|
if (!current) throw failure(ERROR.notFound, "not_found", `No ${kind} entry '${name2}' in the ${scope}`);
|
|
39226
39534
|
if (revision !== void 0 && revision !== toWire(current.revision)) throw failure(ERROR.conflict, "revision_conflict", `${kind} '${name2}' changed in the ${scope} since you opened it`);
|
|
39227
|
-
return deleteEntry(kind, name2, { ...options2, scope, ...revision !== void 0 ? { revision: current.revision } : {} });
|
|
39535
|
+
return deleteEntry(kind, name2, { ...options2, scope, policy, ...revision !== void 0 ? { revision: current.revision } : {} });
|
|
39228
39536
|
})),
|
|
39229
|
-
"library.duplicate": guard(({ kind, from, to, scope, projectRoot }) => serial(async () => {
|
|
39537
|
+
"library.duplicate": guard(({ kind, from, to, scope, projectRoot, policy }) => serial(async () => {
|
|
39230
39538
|
const options2 = await where(projectRoot, scope);
|
|
39231
39539
|
try {
|
|
39232
39540
|
await getEntry(kind, from, options2);
|
|
39233
39541
|
} catch (error51) {
|
|
39234
39542
|
throw fromCore(error51, { notFound: "not_found" });
|
|
39235
39543
|
}
|
|
39236
|
-
return { revision: toWire((await duplicateEntry(kind, from, to, { ...options2, scope })).revision) };
|
|
39544
|
+
return { revision: toWire((await duplicateEntry(kind, from, to, { ...options2, scope, policy })).revision) };
|
|
39237
39545
|
}), { notFound: "invalid_entry" }),
|
|
39238
|
-
"library.rename": guard(({ kind, from, to, scope, projectRoot }) => serial(async () => {
|
|
39546
|
+
"library.rename": guard(({ kind, from, to, scope, projectRoot, policy }) => serial(async () => {
|
|
39239
39547
|
const options2 = await where(projectRoot, scope);
|
|
39240
|
-
return { revision: toWire((await renameEntry(kind, from, to, { ...options2, scope })).revision) };
|
|
39548
|
+
return { revision: toWire((await renameEntry(kind, from, to, { ...options2, scope, policy })).revision) };
|
|
39241
39549
|
})),
|
|
39242
39550
|
"library.skills.set": guard(({ agent, skills }) => serial(() => setGivenSkills(agent, skills, { library: home2 })), { notFound: "invalid_entry" }),
|
|
39243
39551
|
"library.models": guard(async () => {
|
|
@@ -39258,13 +39566,13 @@ function createOperations({ home: home2, templates, knownProjects = () => [] } =
|
|
|
39258
39566
|
const library = home2 !== void 0;
|
|
39259
39567
|
return {
|
|
39260
39568
|
handlers: { ...taskHandlers, ...library ? libraryHandlers : {} },
|
|
39261
|
-
capabilities: [CAPABILITY.task, ...library ? [CAPABILITY.team, CAPABILITY.library, CAPABILITY.settingsLanguage] : []]
|
|
39569
|
+
capabilities: [CAPABILITY.task, ...library ? [CAPABILITY.team, CAPABILITY.library, CAPABILITY.libraryProjectWrite, CAPABILITY.settingsLanguage] : []]
|
|
39262
39570
|
};
|
|
39263
39571
|
}
|
|
39264
39572
|
|
|
39265
39573
|
// packages/daemon/src/files.ts
|
|
39266
39574
|
import { constants as constants3 } from "node:fs";
|
|
39267
|
-
import { lstat as
|
|
39575
|
+
import { lstat as lstat7, open as open4, opendir, realpath as realpath5, stat as stat9 } from "node:fs/promises";
|
|
39268
39576
|
import path34 from "node:path";
|
|
39269
39577
|
|
|
39270
39578
|
// packages/daemon/src/gitignore.ts
|
|
@@ -39367,7 +39675,8 @@ function hasGitEntry(root) {
|
|
|
39367
39675
|
try {
|
|
39368
39676
|
lstatSync2(path33.join(directory, ".git"));
|
|
39369
39677
|
return true;
|
|
39370
|
-
} catch {
|
|
39678
|
+
} catch (error51) {
|
|
39679
|
+
if (error51.code !== "ENOENT") throw new IgnoreUnknown("cannot examine .git");
|
|
39371
39680
|
}
|
|
39372
39681
|
if (path33.dirname(directory) === directory) return false;
|
|
39373
39682
|
}
|
|
@@ -39463,22 +39772,34 @@ function check4(git2, root, paths, deadline, timeoutMs) {
|
|
|
39463
39772
|
if (err.length < GIT_CHECK.maxErrorBytes) err += chunk.toString("utf8");
|
|
39464
39773
|
});
|
|
39465
39774
|
child.on("error", (error51) => finish(() => {
|
|
39466
|
-
|
|
39467
|
-
else reject(new IgnoreUnknown(error51.message));
|
|
39775
|
+
reject(new IgnoreUnknown(error51.message));
|
|
39468
39776
|
}));
|
|
39469
39777
|
child.on("close", (code) => finish(() => {
|
|
39470
39778
|
if (code === 128 && /not a git repository/i.test(err)) {
|
|
39471
|
-
|
|
39472
|
-
|
|
39779
|
+
try {
|
|
39780
|
+
if (hasGitEntry(root)) reject(new IgnoreUnknown("a .git Git cannot use"));
|
|
39781
|
+
else resolve2(null);
|
|
39782
|
+
} catch (error51) {
|
|
39783
|
+
reject(error51);
|
|
39784
|
+
}
|
|
39473
39785
|
return;
|
|
39474
39786
|
}
|
|
39475
39787
|
if (code !== 0 && code !== 1) {
|
|
39476
39788
|
reject(new IgnoreUnknown(`exit ${code}`));
|
|
39477
39789
|
return;
|
|
39478
39790
|
}
|
|
39791
|
+
if (err.length || code === 1 && outBytes || code === 0 && !outBytes) {
|
|
39792
|
+
reject(new IgnoreUnknown("inconsistent exit status or diagnostics"));
|
|
39793
|
+
return;
|
|
39794
|
+
}
|
|
39795
|
+
const output = Buffer.concat(out).toString("utf8");
|
|
39796
|
+
if (outBytes && !output.endsWith("\0")) {
|
|
39797
|
+
reject(new IgnoreUnknown("unterminated answer"));
|
|
39798
|
+
return;
|
|
39799
|
+
}
|
|
39479
39800
|
const asked = new Set(paths);
|
|
39480
39801
|
const ignored = /* @__PURE__ */ new Set();
|
|
39481
|
-
for (const entry of
|
|
39802
|
+
for (const entry of output.split("\0")) {
|
|
39482
39803
|
if (!entry) continue;
|
|
39483
39804
|
const relative = entry.startsWith("./") ? entry.slice(2) : void 0;
|
|
39484
39805
|
if (relative === void 0 || !asked.has(relative)) {
|
|
@@ -39511,7 +39832,7 @@ var HIDDEN_DIRECTORIES = /* @__PURE__ */ new Set([".agents", ".alp", ".claude",
|
|
|
39511
39832
|
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "venv", "env", "virtualenv", "dist", "build", "target", "out", "coverage", "vendor", "__pycache__", ".git"]);
|
|
39512
39833
|
var NONE = Number.MAX_SAFE_INTEGER;
|
|
39513
39834
|
async function identity2(directory) {
|
|
39514
|
-
const info = await
|
|
39835
|
+
const info = await lstat7(directory, { bigint: true });
|
|
39515
39836
|
if (!info.isDirectory() || await realpath5(directory) !== directory) return null;
|
|
39516
39837
|
return `${info.dev}:${info.ino}`;
|
|
39517
39838
|
}
|
|
@@ -39552,7 +39873,7 @@ function rank2(relative, query) {
|
|
|
39552
39873
|
var compare = (a, b) => a.tier - b.tier || a.segment - b.segment || a.offset - b.offset || a.fuzzy - b.fuzzy || a.depth - b.depth || (a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
39553
39874
|
var FilesError = class extends Error {
|
|
39554
39875
|
};
|
|
39555
|
-
async function searchWorkspaceFiles(projectRoot, { query = "", limit = LIMITS.defaultLimit, now: now2 = Date.now } = {}) {
|
|
39876
|
+
async function searchWorkspaceFiles(projectRoot, { query = "", limit = LIMITS.defaultLimit, now: now2 = Date.now, git: git2 } = {}) {
|
|
39556
39877
|
if (!path34.isAbsolute(projectRoot)) throw new FilesError("projectRoot must be an absolute path");
|
|
39557
39878
|
let root;
|
|
39558
39879
|
try {
|
|
@@ -39564,12 +39885,14 @@ async function searchWorkspaceFiles(projectRoot, { query = "", limit = LIMITS.de
|
|
|
39564
39885
|
const wanted = query.trim().replace(/\\/g, "/").replace(/^\.?\/+/, "").toLowerCase();
|
|
39565
39886
|
const count = Math.max(1, Math.min(LIMITS.maxLimit, Math.trunc(limit)));
|
|
39566
39887
|
const deadline = now2() + LIMITS.ms;
|
|
39888
|
+
const realDeadline = Date.now() + LIMITS.ms;
|
|
39567
39889
|
const found = [];
|
|
39568
39890
|
let scanned = 0;
|
|
39569
39891
|
let truncated = false;
|
|
39570
39892
|
let level = [""];
|
|
39571
39893
|
for (let depth = 0; level.length && !truncated; depth++) {
|
|
39572
39894
|
const next = [];
|
|
39895
|
+
const levelFiles = [];
|
|
39573
39896
|
for (const relative of level) {
|
|
39574
39897
|
if (scanned >= LIMITS.scanned || now2() > deadline) {
|
|
39575
39898
|
truncated = true;
|
|
@@ -39588,6 +39911,7 @@ async function searchWorkspaceFiles(projectRoot, { query = "", limit = LIMITS.de
|
|
|
39588
39911
|
}
|
|
39589
39912
|
const name2 = entry.name;
|
|
39590
39913
|
const child = relative ? `${relative}/${name2}` : name2;
|
|
39914
|
+
if (name2.includes("\\") || blockedFilePath(child)) continue;
|
|
39591
39915
|
if (entry.isFile()) {
|
|
39592
39916
|
if (name2.startsWith(".")) continue;
|
|
39593
39917
|
const ranked = rank2(child, wanted);
|
|
@@ -39601,11 +39925,27 @@ async function searchWorkspaceFiles(projectRoot, { query = "", limit = LIMITS.de
|
|
|
39601
39925
|
} catch {
|
|
39602
39926
|
continue;
|
|
39603
39927
|
}
|
|
39604
|
-
|
|
39928
|
+
levelFiles.push(...files);
|
|
39605
39929
|
next.push(...directories2);
|
|
39606
39930
|
if (truncated) break;
|
|
39607
39931
|
}
|
|
39608
|
-
|
|
39932
|
+
const remaining = Math.min(deadline - now2(), realDeadline - Date.now());
|
|
39933
|
+
if (remaining <= 0) {
|
|
39934
|
+
truncated = true;
|
|
39935
|
+
break;
|
|
39936
|
+
}
|
|
39937
|
+
let ignored;
|
|
39938
|
+
try {
|
|
39939
|
+
ignored = await gitIgnored(root, [...levelFiles.map((file3) => file3.path), ...next], {
|
|
39940
|
+
...git2,
|
|
39941
|
+
timeoutMs: Math.min(git2?.timeoutMs ?? GIT_CHECK.timeoutMs, remaining)
|
|
39942
|
+
});
|
|
39943
|
+
} catch (error51) {
|
|
39944
|
+
if (!(error51 instanceof IgnoreUnknown)) throw error51;
|
|
39945
|
+
return { files: [], truncated: true };
|
|
39946
|
+
}
|
|
39947
|
+
found.push(...levelFiles.filter((file3) => !ignored?.has(file3.path)));
|
|
39948
|
+
level = next.filter((directory) => !ignored?.has(directory));
|
|
39609
39949
|
}
|
|
39610
39950
|
found.sort(compare);
|
|
39611
39951
|
return { files: found.slice(0, count).map((entry) => ({ path: entry.path })), ...truncated ? { truncated: true } : {} };
|
|
@@ -39652,7 +39992,7 @@ async function directoryChain(root, segments, relative) {
|
|
|
39652
39992
|
async function directoryIdentity(directory, relative) {
|
|
39653
39993
|
let info;
|
|
39654
39994
|
try {
|
|
39655
|
-
info = await
|
|
39995
|
+
info = await lstat7(directory, { bigint: true });
|
|
39656
39996
|
} catch {
|
|
39657
39997
|
throw notFound(relative);
|
|
39658
39998
|
}
|
|
@@ -39688,7 +40028,7 @@ async function ignoredOf(root, paths, git2) {
|
|
|
39688
40028
|
}
|
|
39689
40029
|
}
|
|
39690
40030
|
async function readConfined(file3, maxBytes, probe) {
|
|
39691
|
-
const before = await
|
|
40031
|
+
const before = await lstat7(file3, { bigint: true });
|
|
39692
40032
|
if (before.isSymbolicLink()) throw Object.assign(new Error("link"), { linked: true });
|
|
39693
40033
|
if (!before.isFile()) throw new Error("not a file");
|
|
39694
40034
|
if (before.nlink > 1n) throw Object.assign(new Error("hard link"), { linked: true });
|
|
@@ -39714,7 +40054,7 @@ async function readConfined(file3, maxBytes, probe) {
|
|
|
39714
40054
|
}
|
|
39715
40055
|
if (length > maxBytes) return void 0;
|
|
39716
40056
|
await probe?.("read");
|
|
39717
|
-
const after = await
|
|
40057
|
+
const after = await lstat7(file3, { bigint: true });
|
|
39718
40058
|
if (after.dev !== before.dev || after.ino !== before.ino) throw Object.assign(new Error("changed"), { linked: true });
|
|
39719
40059
|
return { bytes: buffer.subarray(0, length), modifiedAt: opened.mtime };
|
|
39720
40060
|
} finally {
|
|
@@ -39771,7 +40111,7 @@ async function listWorkspaceDirectory(projectRoot, { path: relative, includeIgno
|
|
|
39771
40111
|
}
|
|
39772
40112
|
let info;
|
|
39773
40113
|
try {
|
|
39774
|
-
info = await
|
|
40114
|
+
info = await lstat7(path34.join(directory, name2));
|
|
39775
40115
|
} catch {
|
|
39776
40116
|
continue;
|
|
39777
40117
|
}
|
|
@@ -39912,13 +40252,19 @@ function createDaemonServer({ runtime, socketPath, version: version2, onShutdown
|
|
|
39912
40252
|
if (event.type === "session.opened") {
|
|
39913
40253
|
record3 ??= { version: 1, id: sessionId, rootId: root, status: "initializing", createdAt: now2, updatedAt: now2 };
|
|
39914
40254
|
const usage = event.session.usage ?? record3.session?.usage;
|
|
39915
|
-
|
|
40255
|
+
const parked = recovering.has(root) ? record3.session?.parked : event.session.parked;
|
|
40256
|
+
save2(record3, { rootId: root, session: { ...event.session, ...usage ? { usage } : {}, ...parked ? { parked } : {} }, status: "idle", lastError: void 0, activeTurnId: void 0 });
|
|
39916
40257
|
return false;
|
|
39917
40258
|
}
|
|
39918
40259
|
if (!record3) return false;
|
|
39919
40260
|
if (event.type === "session.updated") save2(record3, { session: { ...record3.session, ...event.session, ...event.session.parked ? {} : { parked: void 0 } } });
|
|
39920
40261
|
else if (event.type === "turn.started") save2(record3, { status: "running", activeTurnId: event.turnId, archived: void 0 });
|
|
39921
|
-
else if (event.type === "turn.ended") save2(record3, {
|
|
40262
|
+
else if (event.type === "turn.ended") save2(record3, {
|
|
40263
|
+
status: "idle",
|
|
40264
|
+
activeTurnId: void 0,
|
|
40265
|
+
...record3.session ? { session: { ...record3.session, parked: event.state === "failed" ? runtime.snapshot(sessionId)?.parked : void 0 } } : {},
|
|
40266
|
+
...event.state === "failed" ? { lastError: { message: event.error?.message ?? "Turn failed" } } : {}
|
|
40267
|
+
});
|
|
39922
40268
|
else if (event.type === "usage" && record3.session) record3.session = { ...record3.session, usage: event.usage };
|
|
39923
40269
|
else if (event.type === "session.failed") save2(record3, { status: "error", lastError: event.error });
|
|
39924
40270
|
else if (event.type === "session.closed") save2(record3, { status: record3.status === "error" ? "error" : "closed", activeTurnId: void 0 });
|
|
@@ -39983,7 +40329,7 @@ function createDaemonServer({ runtime, socketPath, version: version2, onShutdown
|
|
|
39983
40329
|
for (const [root, tree] of byRoot) {
|
|
39984
40330
|
const head = tree.find((record3) => record3.id === root);
|
|
39985
40331
|
if (removed.has(root)) continue;
|
|
39986
|
-
if (head && resumable(head) && (head.status === "running" || tree.some((record3) => continuing.has(record3.id)))) reopen.push({ record: head, working: head.status === "running" });
|
|
40332
|
+
if (head && resumable(head) && (head.status === "running" || head.status === "idle" && head.session?.parked || tree.some((record3) => continuing.has(record3.id)))) reopen.push({ record: head, working: head.status === "running" });
|
|
39987
40333
|
if (tree.some((record3) => LIVE.includes(record3.status))) seen(root, await store2.timeline(root));
|
|
39988
40334
|
for (const record3 of tree) {
|
|
39989
40335
|
records.set(record3.id, record3);
|
|
@@ -39991,7 +40337,7 @@ function createDaemonServer({ runtime, socketPath, version: version2, onShutdown
|
|
|
39991
40337
|
const envelope = (event) => ({ sessionId: record3.id, epoch, seq: 0, ts: (/* @__PURE__ */ new Date()).toISOString(), event });
|
|
39992
40338
|
if (record3.activeTurnId) keep(root, envelope({ type: "turn.ended", turnId: record3.activeTurnId, state: "failed", error: { message: ended.message, code: ended.code } }));
|
|
39993
40339
|
if (record3.id === root) {
|
|
39994
|
-
save2(record3, record3.status === "idle" ? { status: "closed", activeTurnId: void 0 } : { status: "error", lastError: ended, activeTurnId: void 0 });
|
|
40340
|
+
if (!(record3.status === "idle" && record3.session?.parked)) save2(record3, record3.status === "idle" ? { status: "closed", activeTurnId: void 0 } : { status: "error", lastError: ended, activeTurnId: void 0 });
|
|
39995
40341
|
} else if (continuing.has(record3.id) && reopen.some((entry) => entry.record.id === root)) {
|
|
39996
40342
|
save2(record3, { status: "closed", activeTurnId: void 0 });
|
|
39997
40343
|
} else {
|
|
@@ -40086,7 +40432,7 @@ function createDaemonServer({ runtime, socketPath, version: version2, onShutdown
|
|
|
40086
40432
|
persist: true,
|
|
40087
40433
|
restore: { agent: session.agent, threadId: session.threadId, runtime: session.runtime, model: session.model, workflow: session.workflow }
|
|
40088
40434
|
}, { history: "skip", delegation: record3.delegation ?? true });
|
|
40089
|
-
const outcomes = await runtime.recover(record3.id, { continueRoot: working });
|
|
40435
|
+
const outcomes = await runtime.recover(record3.id, { continueRoot: working, parkedRoot: session.parked });
|
|
40090
40436
|
recoveryLog.info(`recovered ${record3.id}: ${outcomes.map((outcome) => `${outcome.agent} ${outcome.outcome}`).join(", ") || "root"}`, { event: "recovered", sessionId: record3.id, rootId: record3.rootId });
|
|
40091
40437
|
} catch (error51) {
|
|
40092
40438
|
recoveryLog.error(`could not recover ${record3.id}`, { code: "ALP_RECOVERY_FAILED", sessionId: record3.id, rootId: record3.rootId, error: error51 });
|
|
@@ -40809,7 +41155,7 @@ ${session.id}`.toLowerCase().includes(word))).sort((a, b) => listCursor.compare(
|
|
|
40809
41155
|
async listen() {
|
|
40810
41156
|
if (process.platform !== "win32") {
|
|
40811
41157
|
await assertPrivateDirectory(path35.dirname(socketPath), { create: true });
|
|
40812
|
-
const existing = await
|
|
41158
|
+
const existing = await lstat8(socketPath).catch((error51) => {
|
|
40813
41159
|
if (error51.code === "ENOENT") return void 0;
|
|
40814
41160
|
throw error51;
|
|
40815
41161
|
});
|
|
@@ -40848,8 +41194,8 @@ ${session.id}`.toLowerCase().includes(word))).sort((a, b) => listCursor.compare(
|
|
|
40848
41194
|
if (process.platform !== "win32") {
|
|
40849
41195
|
try {
|
|
40850
41196
|
await chmod(bindPath, 384);
|
|
40851
|
-
const info = await
|
|
40852
|
-
await
|
|
41197
|
+
const info = await lstat8(bindPath);
|
|
41198
|
+
await rename8(bindPath, socketPath);
|
|
40853
41199
|
socketIdentity = { dev: info.dev, ino: info.ino };
|
|
40854
41200
|
} catch (error51) {
|
|
40855
41201
|
await new Promise((resolve2) => server.close(() => resolve2()));
|
|
@@ -40932,7 +41278,7 @@ ${session.id}`.toLowerCase().includes(word))).sort((a, b) => listCursor.compare(
|
|
|
40932
41278
|
for (const connection of connections) connection.socket?.destroy();
|
|
40933
41279
|
await stopped;
|
|
40934
41280
|
if (socketIdentity) {
|
|
40935
|
-
const current2 = await
|
|
41281
|
+
const current2 = await lstat8(socketPath).catch((error51) => {
|
|
40936
41282
|
if (error51.code === "ENOENT") return void 0;
|
|
40937
41283
|
throw error51;
|
|
40938
41284
|
});
|
|
@@ -40945,14 +41291,14 @@ ${session.id}`.toLowerCase().includes(word))).sort((a, b) => listCursor.compare(
|
|
|
40945
41291
|
}
|
|
40946
41292
|
|
|
40947
41293
|
// packages/daemon/src/lock.ts
|
|
40948
|
-
import { unlink as unlink5, utimes, writeFile as
|
|
41294
|
+
import { unlink as unlink5, utimes, writeFile as writeFile16, rename as rename9 } from "node:fs/promises";
|
|
40949
41295
|
async function acquireLock(home2, contents) {
|
|
40950
41296
|
return acquireDaemonLock(home2, contents);
|
|
40951
41297
|
}
|
|
40952
41298
|
async function replaceFile(temporary, file3) {
|
|
40953
41299
|
for (let attempt = 0; ; attempt++) {
|
|
40954
41300
|
try {
|
|
40955
|
-
return await
|
|
41301
|
+
return await rename9(temporary, file3);
|
|
40956
41302
|
} catch (error51) {
|
|
40957
41303
|
if (process.platform !== "win32" || attempt >= 20 || !["EPERM", "EACCES", "EBUSY"].includes(error51?.code)) throw error51;
|
|
40958
41304
|
await new Promise((resolve2) => setTimeout(resolve2, 10 + attempt * 10));
|
|
@@ -40962,7 +41308,7 @@ async function replaceFile(temporary, file3) {
|
|
|
40962
41308
|
async function updateLock(home2, lock) {
|
|
40963
41309
|
const file3 = daemonPaths(home2).lock;
|
|
40964
41310
|
const temporary = `${file3}.${process.pid}.tmp`;
|
|
40965
|
-
await
|
|
41311
|
+
await writeFile16(temporary, JSON.stringify(lock), { mode: 384 });
|
|
40966
41312
|
await replaceFile(temporary, file3);
|
|
40967
41313
|
}
|
|
40968
41314
|
async function heartbeat(home2) {
|
|
@@ -40980,7 +41326,7 @@ async function releaseLock(home2) {
|
|
|
40980
41326
|
}
|
|
40981
41327
|
|
|
40982
41328
|
// packages/daemon/src/store.ts
|
|
40983
|
-
import { appendFile as appendFile4, mkdir as mkdir15, open as open5, readdir as
|
|
41329
|
+
import { appendFile as appendFile4, mkdir as mkdir15, open as open5, readdir as readdir10, readFile as readFile20, rm as rm6, stat as stat10, writeFile as writeFile17 } from "node:fs/promises";
|
|
40984
41330
|
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
40985
41331
|
import path36 from "node:path";
|
|
40986
41332
|
var log3 = createLogger({ component: "store" });
|
|
@@ -41009,7 +41355,7 @@ function createStore(root) {
|
|
|
41009
41355
|
async function writeAtomic3(file3, body) {
|
|
41010
41356
|
const temporary = `${file3}.${process.pid}.${randomBytes8(4).toString("hex")}.tmp`;
|
|
41011
41357
|
try {
|
|
41012
|
-
await
|
|
41358
|
+
await writeFile17(temporary, body, { mode: 384 });
|
|
41013
41359
|
await replaceFile(temporary, file3);
|
|
41014
41360
|
} catch (error51) {
|
|
41015
41361
|
await rm6(temporary, { force: true }).catch(() => {
|
|
@@ -41126,7 +41472,7 @@ function createStore(root) {
|
|
|
41126
41472
|
async receipts() {
|
|
41127
41473
|
await prepare2();
|
|
41128
41474
|
const found = [];
|
|
41129
|
-
for (const name2 of await
|
|
41475
|
+
for (const name2 of await readdir10(receiptsDir)) {
|
|
41130
41476
|
if (!name2.endsWith(".json")) continue;
|
|
41131
41477
|
const key2 = name2.slice(0, -".json".length);
|
|
41132
41478
|
try {
|
|
@@ -41152,7 +41498,7 @@ function createStore(root) {
|
|
|
41152
41498
|
await prepare2();
|
|
41153
41499
|
const now2 = Date.now();
|
|
41154
41500
|
for (const directory of [sessionsDir, receiptsDir]) {
|
|
41155
|
-
for (const name2 of await
|
|
41501
|
+
for (const name2 of await readdir10(directory)) {
|
|
41156
41502
|
const file3 = path36.join(directory, name2);
|
|
41157
41503
|
if (name2.endsWith(".tmp")) {
|
|
41158
41504
|
if (temporaries) await rm6(file3, { force: true });
|
|
@@ -41167,7 +41513,7 @@ function createStore(root) {
|
|
|
41167
41513
|
async list() {
|
|
41168
41514
|
await prepare2();
|
|
41169
41515
|
const records = [];
|
|
41170
|
-
for (const name2 of await
|
|
41516
|
+
for (const name2 of await readdir10(sessionsDir)) {
|
|
41171
41517
|
if (!name2.endsWith(".json")) continue;
|
|
41172
41518
|
try {
|
|
41173
41519
|
const record3 = JSON.parse(await readFile20(path36.join(sessionsDir, name2), "utf8"));
|
|
@@ -41219,7 +41565,7 @@ function createStore(root) {
|
|
|
41219
41565
|
let step = "write";
|
|
41220
41566
|
try {
|
|
41221
41567
|
await prepare2();
|
|
41222
|
-
await
|
|
41568
|
+
await writeFile17(file3, token, { mode: 384 });
|
|
41223
41569
|
step = "read";
|
|
41224
41570
|
if (await readFile20(file3, "utf8") !== token) throw new Error("what was written is not what was read back");
|
|
41225
41571
|
return { ok: true };
|
|
@@ -41257,7 +41603,7 @@ function createStore(root) {
|
|
|
41257
41603
|
// packages/core/src/backup.js
|
|
41258
41604
|
import { createHash as createHash12, randomBytes as randomBytes9 } from "node:crypto";
|
|
41259
41605
|
import { createReadStream, createWriteStream } from "node:fs";
|
|
41260
|
-
import { chmod as chmod2, lstat as
|
|
41606
|
+
import { chmod as chmod2, lstat as lstat9, mkdir as mkdir17, open as open6, readdir as readdir12, readFile as readFile22, readlink as readlink2, rm as rm7, rmdir as rmdir3, stat as stat11, symlink as symlink2, utimes as utimes2 } from "node:fs/promises";
|
|
41261
41607
|
import path38 from "node:path";
|
|
41262
41608
|
import { Readable } from "node:stream";
|
|
41263
41609
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -41265,7 +41611,7 @@ import { pipeline } from "node:stream/promises";
|
|
|
41265
41611
|
import { createGunzip, createGzip } from "node:zlib";
|
|
41266
41612
|
|
|
41267
41613
|
// packages/core/src/state-schema.js
|
|
41268
|
-
import { readdir as
|
|
41614
|
+
import { readdir as readdir11, readFile as readFile21, mkdir as mkdir16 } from "node:fs/promises";
|
|
41269
41615
|
import path37 from "node:path";
|
|
41270
41616
|
var STATE_SCHEMA = 2;
|
|
41271
41617
|
var stateDir = (home2) => path37.join(home2, "state");
|
|
@@ -41279,7 +41625,7 @@ async function readStateSchema(home2) {
|
|
|
41279
41625
|
} catch (error51) {
|
|
41280
41626
|
if (error51?.code !== "ENOENT") throw error51;
|
|
41281
41627
|
for (const name2 of DATA) {
|
|
41282
|
-
const entries2 = await
|
|
41628
|
+
const entries2 = await readdir11(path37.join(stateDir(home2), name2)).catch((error52) => {
|
|
41283
41629
|
if (error52?.code === "ENOENT") return [];
|
|
41284
41630
|
throw error52;
|
|
41285
41631
|
});
|
|
@@ -41336,7 +41682,7 @@ var transient = (name2) => name2.endsWith(".tmp") || name2 === ".lock" || /\.pre
|
|
|
41336
41682
|
var backupsDir = (home2) => path38.join(home2, "backups");
|
|
41337
41683
|
var journalFile = (home2) => path38.join(home2, JOURNAL);
|
|
41338
41684
|
var stamp2 = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
41339
|
-
var exists3 = (file3) =>
|
|
41685
|
+
var exists3 = (file3) => lstat9(file3).then(() => true, () => false);
|
|
41340
41686
|
var invalid = (message, options2) => new AlpError("BACKUP_INVALID", message, options2);
|
|
41341
41687
|
function safeRelative(value) {
|
|
41342
41688
|
return typeof value === "string" && value.length > 0 && value.length < 4096 && !value.startsWith("/") && !/^[A-Za-z]:/.test(value) && !value.includes("\\") && !value.includes("\0") && value.split("/").every((part) => part && part !== "." && part !== "..");
|
|
@@ -41344,7 +41690,7 @@ function safeRelative(value) {
|
|
|
41344
41690
|
async function sessionRecords(home2) {
|
|
41345
41691
|
const directory = path38.join(home2, "state", "sessions");
|
|
41346
41692
|
const records = [];
|
|
41347
|
-
for (const name2 of await
|
|
41693
|
+
for (const name2 of await readdir12(directory).catch(() => [])) {
|
|
41348
41694
|
if (!name2.endsWith(".json")) continue;
|
|
41349
41695
|
try {
|
|
41350
41696
|
records.push(JSON.parse(await readFile22(path38.join(directory, name2), "utf8")));
|
|
@@ -41363,7 +41709,7 @@ async function externalReferences(home2) {
|
|
|
41363
41709
|
}
|
|
41364
41710
|
async function* unitEntries(base2, unit) {
|
|
41365
41711
|
const full = path38.join(base2, unit);
|
|
41366
|
-
const info = await
|
|
41712
|
+
const info = await lstat9(full).catch((error51) => {
|
|
41367
41713
|
if (error51?.code === "ENOENT") return void 0;
|
|
41368
41714
|
throw error51;
|
|
41369
41715
|
});
|
|
@@ -41373,9 +41719,9 @@ async function* unitEntries(base2, unit) {
|
|
|
41373
41719
|
if (info2.isSymbolicLink()) yield { t: "link", path: relative, target: await readlink2(file3) };
|
|
41374
41720
|
else if (info2.isDirectory()) {
|
|
41375
41721
|
yield { t: "dir", path: relative, mode: info2.mode & 511 };
|
|
41376
|
-
for (const name2 of (await
|
|
41722
|
+
for (const name2 of (await readdir12(file3)).sort()) {
|
|
41377
41723
|
if (transient(name2)) continue;
|
|
41378
|
-
const child = await
|
|
41724
|
+
const child = await lstat9(path38.join(file3, name2)).catch(() => void 0);
|
|
41379
41725
|
if (child) yield* walk(`${relative}/${name2}`, child);
|
|
41380
41726
|
}
|
|
41381
41727
|
} else if (info2.isFile()) yield { t: "file", path: relative, mode: info2.mode & 511, file: file3 };
|
|
@@ -41383,7 +41729,7 @@ async function* unitEntries(base2, unit) {
|
|
|
41383
41729
|
yield* walk(unit, info);
|
|
41384
41730
|
}
|
|
41385
41731
|
async function unitKind(base2, unit) {
|
|
41386
|
-
const info = await
|
|
41732
|
+
const info = await lstat9(path38.join(base2, unit)).catch((error51) => {
|
|
41387
41733
|
if (error51?.code === "ENOENT") return void 0;
|
|
41388
41734
|
throw error51;
|
|
41389
41735
|
});
|
|
@@ -41459,7 +41805,7 @@ async function automaticBackup(home2, kind, { units = HOME_UNITS, projects = [],
|
|
|
41459
41805
|
const out = path38.join(directory, `${kind}-${stamp2()}-${randomBytes9(2).toString("hex")}${BACKUP_EXTENSION}`);
|
|
41460
41806
|
await createBackup({ home: home2, out, units, projects, by, kind });
|
|
41461
41807
|
const prefix = `${kind}-`;
|
|
41462
|
-
const same2 = (await
|
|
41808
|
+
const same2 = (await readdir12(directory)).filter((name2) => name2.startsWith(prefix) && name2.endsWith(BACKUP_EXTENSION)).sort();
|
|
41463
41809
|
for (const name2 of same2.slice(0, Math.max(0, same2.length - AUTOMATIC_KEEP))) await rm7(path38.join(directory, name2), { force: true });
|
|
41464
41810
|
return out;
|
|
41465
41811
|
}
|
|
@@ -41522,7 +41868,7 @@ async function readBackup(file3, { onHeader, visit }) {
|
|
|
41522
41868
|
const unit = units.find((candidate) => record3.path === candidate.path || record3.path.startsWith(`${candidate.path}/`));
|
|
41523
41869
|
if (!unit || unit.kind === "absent" || record3.path !== unit.path && unit.kind !== "dir") throw invalid(`${record3.path} lies outside the units the backup declares`);
|
|
41524
41870
|
const seen = written.get(area) ?? written.set(area, { paths: /* @__PURE__ */ new Set(), links: [] }).get(area);
|
|
41525
|
-
if (seen.paths.has(record3.path) || seen.links.some((
|
|
41871
|
+
if (seen.paths.has(record3.path) || seen.links.some((link2) => record3.path.startsWith(`${link2}/`))) throw invalid(`${record3.path} comes twice, or lies under a link`);
|
|
41526
41872
|
seen.paths.add(record3.path);
|
|
41527
41873
|
if (record3.t === "link") seen.links.push(record3.path);
|
|
41528
41874
|
if (record3.t === "dir" || record3.t === "link") {
|
|
@@ -41597,9 +41943,9 @@ async function settleJournal(journal, { releaseLocks = false } = {}) {
|
|
|
41597
41943
|
if (journal.marker) await rm7(journal.marker, { force: true });
|
|
41598
41944
|
}
|
|
41599
41945
|
for (const directory of journal.staging) await rm7(directory, { recursive: true, force: true });
|
|
41600
|
-
for (const directory of journal.created ?? []) await
|
|
41946
|
+
for (const directory of journal.created ?? []) await rmdir3(directory).catch(() => {
|
|
41601
41947
|
});
|
|
41602
|
-
if (releaseLocks) for (const lock of journal.locks ?? []) await
|
|
41948
|
+
if (releaseLocks) for (const lock of journal.locks ?? []) await rmdir3(lock).catch(() => {
|
|
41603
41949
|
});
|
|
41604
41950
|
}
|
|
41605
41951
|
async function recoverInterruptedRestore(home2) {
|
|
@@ -41711,7 +42057,7 @@ async function restoreBackup({ home: home2, file: file3, projects = true, safety
|
|
|
41711
42057
|
}
|
|
41712
42058
|
for (const [area, root] of staging) {
|
|
41713
42059
|
const base2 = area === "home" ? "state/sessions" : ".alp/tasks";
|
|
41714
|
-
for (const name2 of await
|
|
42060
|
+
for (const name2 of await readdir12(path38.join(root, ...base2.split("/"))).catch(() => [])) {
|
|
41715
42061
|
if (!name2.endsWith(".json")) continue;
|
|
41716
42062
|
try {
|
|
41717
42063
|
JSON.parse(await readFile22(path38.join(root, ...base2.split("/"), name2), "utf8"));
|
|
@@ -41825,11 +42171,11 @@ async function clearRestoredMarker(home2) {
|
|
|
41825
42171
|
}
|
|
41826
42172
|
|
|
41827
42173
|
// packages/core/src/migrations.js
|
|
41828
|
-
import { readdir as
|
|
42174
|
+
import { readdir as readdir13, readFile as readFile23, rm as rm8, stat as stat12 } from "node:fs/promises";
|
|
41829
42175
|
import path39 from "node:path";
|
|
41830
42176
|
async function timelineCursors(home2) {
|
|
41831
42177
|
const directory = path39.join(stateDir(home2), "timeline");
|
|
41832
|
-
const names = await
|
|
42178
|
+
const names = await readdir13(directory).catch((error51) => {
|
|
41833
42179
|
if (error51?.code === "ENOENT") return [];
|
|
41834
42180
|
throw error51;
|
|
41835
42181
|
});
|
|
@@ -41913,7 +42259,7 @@ async function migrateState(home2, { by = "alp", migrations = MIGRATIONS, target
|
|
|
41913
42259
|
}
|
|
41914
42260
|
|
|
41915
42261
|
// packages/daemon/src/main.ts
|
|
41916
|
-
var VERSION = true ? "0.0.
|
|
42262
|
+
var VERSION = true ? "0.0.1-beta.3" : "0.0.0-dev";
|
|
41917
42263
|
var LOG_BYTES = 10 * 1024 * 1024;
|
|
41918
42264
|
var LOG_FILES = 3;
|
|
41919
42265
|
var log4 = createLogger({ component: "alpd" });
|
|
@@ -41921,7 +42267,7 @@ async function rotate(log5) {
|
|
|
41921
42267
|
const size = await stat13(log5).then((info) => info.size, () => 0);
|
|
41922
42268
|
if (size < LOG_BYTES) return;
|
|
41923
42269
|
for (let index = LOG_FILES - 1; index >= 1; index--) {
|
|
41924
|
-
await
|
|
42270
|
+
await rename10(index === 1 ? log5 : `${log5}.${index - 1}`, `${log5}.${index}`).catch(() => {
|
|
41925
42271
|
});
|
|
41926
42272
|
}
|
|
41927
42273
|
}
|
|
@@ -41933,12 +42279,39 @@ async function detach(home2) {
|
|
|
41933
42279
|
const output = await open7(log5, "a", 384);
|
|
41934
42280
|
const child = spawn9(process.execPath, [fileURLToPath(import.meta.url)], {
|
|
41935
42281
|
detached: true,
|
|
41936
|
-
stdio: ["ignore", output.fd, output.fd],
|
|
42282
|
+
stdio: ["ignore", output.fd, output.fd, "ipc"],
|
|
41937
42283
|
env: process.env,
|
|
41938
42284
|
windowsHide: true
|
|
41939
42285
|
});
|
|
41940
|
-
child.unref();
|
|
41941
42286
|
await output.close();
|
|
42287
|
+
await new Promise((resolve2, reject) => {
|
|
42288
|
+
const requested = Number(process.argv.find((arg) => arg.startsWith("--startup-timeout="))?.split("=")[1]);
|
|
42289
|
+
const timeoutMs = Number.isFinite(requested) && requested > 0 ? requested : 15e3;
|
|
42290
|
+
let timedOut = false;
|
|
42291
|
+
let force;
|
|
42292
|
+
const timer = setTimeout(() => {
|
|
42293
|
+
timedOut = true;
|
|
42294
|
+
child.kill();
|
|
42295
|
+
force = setTimeout(() => child.kill("SIGKILL"), 1e3);
|
|
42296
|
+
}, timeoutMs);
|
|
42297
|
+
child.once("error", (error51) => {
|
|
42298
|
+
clearTimeout(timer);
|
|
42299
|
+
clearTimeout(force);
|
|
42300
|
+
reject(error51);
|
|
42301
|
+
});
|
|
42302
|
+
child.once("exit", (code, signal) => {
|
|
42303
|
+
clearTimeout(timer);
|
|
42304
|
+
clearTimeout(force);
|
|
42305
|
+
reject(new Error(timedOut ? `alpd startup timed out after ${timeoutMs} ms` : `alpd exited during startup (${code ?? signal})`));
|
|
42306
|
+
});
|
|
42307
|
+
child.once("message", (message) => {
|
|
42308
|
+
if (message !== "alpd.ready" || timedOut) return;
|
|
42309
|
+
clearTimeout(timer);
|
|
42310
|
+
child.disconnect();
|
|
42311
|
+
child.unref();
|
|
42312
|
+
resolve2();
|
|
42313
|
+
});
|
|
42314
|
+
});
|
|
41942
42315
|
}
|
|
41943
42316
|
async function serviceLog(home2) {
|
|
41944
42317
|
await assertPrivateDirectory(home2, { create: true });
|
|
@@ -42042,14 +42415,12 @@ async function run2(home2) {
|
|
|
42042
42415
|
await stopLeftovers(await readFile24(marker, "utf8").then(JSON.parse).catch(() => void 0));
|
|
42043
42416
|
}
|
|
42044
42417
|
await mkdir18(path40.dirname(marker), { recursive: true, mode: 448 });
|
|
42045
|
-
await
|
|
42418
|
+
await writeFile18(marker, JSON.stringify({ pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString(), groupLeader: leadsGroup() }), { mode: 384 });
|
|
42046
42419
|
const runLogDir = process.env.ALP_RUN_LOG_DIR || path40.join(home2, "runs");
|
|
42047
42420
|
const worktreeDir = path40.join(home2, "worktrees");
|
|
42048
42421
|
for (const branch of await reclaimWorktrees(worktreeDir).catch(() => [])) log4.info(`kept interrupted work on branch ${branch}`, { event: "worktree.kept", branch });
|
|
42049
42422
|
const copyDir = path40.join(home2, "copies");
|
|
42050
42423
|
await reclaimCopies(copyDir).catch(() => 0);
|
|
42051
|
-
await unlink6(path40.join(home2, "state", "alpd.held")).catch(() => {
|
|
42052
|
-
});
|
|
42053
42424
|
const settings = await userSettings(home2);
|
|
42054
42425
|
const runtime = createAlpRuntime({
|
|
42055
42426
|
templates: templates_default,
|
|
@@ -42114,16 +42485,24 @@ async function run2(home2) {
|
|
|
42114
42485
|
phase = "ready";
|
|
42115
42486
|
await updateLock(home2, { ...lock, ready: true });
|
|
42116
42487
|
const { install } = daemonPaths(home2);
|
|
42117
|
-
await
|
|
42488
|
+
await writeFile18(`${install}.tmp`, JSON.stringify({ entry: fileURLToPath(import.meta.url), version: VERSION }), { mode: 384 }).then(() => rename10(`${install}.tmp`, install)).catch((error51) => log4.warn("could not record alpd's location", { code: "ALP_INSTALL_RECORD_FAILED", error: error51 }));
|
|
42118
42489
|
if (opened.restored) await clearRestoredMarker(home2).catch((error51) => log4.warn("could not clear the restore marker", { code: "ALP_STATE_RESTORED", error: error51 }));
|
|
42490
|
+
if (process.connected) process.send?.("alpd.ready");
|
|
42119
42491
|
log4.info(`alpd ${VERSION} ready on ${socket} (pid ${process.pid})`, { event: "ready", version: VERSION, pid: process.pid });
|
|
42120
42492
|
}
|
|
42121
42493
|
var home = alpHome();
|
|
42122
42494
|
try {
|
|
42123
42495
|
if (process.argv.includes("--detach")) await detach(home);
|
|
42124
42496
|
else {
|
|
42497
|
+
if (!process.connected) await unlink6(path40.join(home, "state", "alpd.held")).catch(() => {
|
|
42498
|
+
});
|
|
42125
42499
|
if (process.argv.includes("--service")) await serviceLog(home);
|
|
42126
|
-
await run2(home);
|
|
42500
|
+
if (process.connected) await run2(home);
|
|
42501
|
+
else {
|
|
42502
|
+
await withDaemonLifecycle(home, "start", async () => {
|
|
42503
|
+
await run2(home);
|
|
42504
|
+
}, { joinStart: true });
|
|
42505
|
+
}
|
|
42127
42506
|
}
|
|
42128
42507
|
} catch (error51) {
|
|
42129
42508
|
const stoppedIn = phase;
|