Étendre Orkeon Code
Tout ce que l'agent dit et fait côté TypeScript est un fichier source de ce dépôt : une commande est un fichier, un
crew un dossier, un prompt une chaîne avec son test sous tests/, un mode de permission une ligne d'une table. Pas d'étape
de build de votre côté : le runtime bundle les sources avec esbuild et les exécute dans Jint au démarrage du REPL, et
un changement est donc actif au prochain démarrage. Cette page liste les points d'extension, ce que chacun touche, et
où sont les limites. Les couches, les ports et les recettes pas à pas sont dans
Architecture ; les règles de code dans CONTRIBUTING.md.
Une commande
Un fichier src/commands/<group>/<name>.cmd.ts, <group> l'un des dossiers fonctionnels (session, settings,
status, project, git, dev, memory, speech). Le chargeur prend chaque *.cmd.ts sous le dossier ; le nom est en kebab-case et unique, et
le fichier n'exporte rien. Une commande synchrone répond tout de suite avec defineCommand ; une commande asynchrone
lance un crew avec defineAsyncCommand et affiche le résultat quand le crew se termine
(Architecture). Une commande atteint le runtime par les adaptateurs de
src/shared/infrastructure/orkeon/wiring.ts, jamais par les globales :
/// <reference path="./.orkeon/orkeon-cli.d.ts" />
import { orkeonSessionIo } from "../shared/infrastructure/orkeon/wiring.ts";
const session = orkeonSessionIo();
defineCommand({
name: "which-mode",
description: "Say which permission mode the next request runs in, and why.",
handler: async (_args, ctx) => {
const { mode, via } = await session.resolveEffectiveSessionMode();
return ctx.continue(`${mode} (${via})`);
},
});
La logique qui mérite un test va dans src/commands/_lib/<group>/<name>.ts, le dossier de sa commande,
sous la forme run<Name>(tokens, deps), ses deps un Pick<> des ports qu'elle lit, testée sur les fakes de tests/support/doubles/. Trois autres endroits bougent avec
une nouvelle commande, et npm run check nomme celui que vous avez oublié : tests/repository/command-surface.test.ts épingle
le nombre de commandes, src/commands/_lib/status/meta.ts l'énonce dans les notes de release, et npm run docs:commands
régénère docs/fr/commands.md. La recette : Ajouter une commande.
Un crew
Un dossier src/crews/<name>/ avec un crew.ork.ts dans la forme procédurale : les agents avec les noms d'outils
dont ils ont besoin, un agent dont le .body() appelle le cas d'usage, publishCrew(...) à la fin. Le cas d'usage
est application/run-<name>.ts (run<Name>(input, deps)), ses règles pures dans domain/, chacune avec son
*.test.ts sous tests/crews/<name>/ ; le test du cas d'usage tourne sur les fakes. La commande qui le lance le nomme,
launchCrew({ crews: createCrewHost(ctx.services), log: ctx.log }, "<name>", input), et input arrive au crew comme
inputs(). ; un crew qui ne doit pas tourner deux fois à la fois est lancé avec { exclusive: true } en cinquième
argument, et l'hôte refuse le second lancement avec le ticket en cours (/review, /memory admit). Les imports sont relatifs et portent leur extension .ts, parce que le runtime bundle chaque point
d'entrée à partir des fichiers tels qu'ils sont écrits.
advisorest le modèle le plus petit : un agent, unctx.llm.complete, pas d'outil.code-reviewest le modèle d'un crew séquentiel qui litinputs()et lance des outils.main-loopest le modèle d'une boucle :ctx.llm.actfait tourner le cycle modèle ⇄ outils sur le catalogue de l'agent, sous.budget(...).
Un dossier qui commence par _ est une sonde de développement, jamais listée aux utilisateurs. Lancez n'importe quel
crew seul avec bash scripts/run-crew.sh src/crews/<name>/crew.ork.ts --inputs '{...}'
(Exécutions autonomes et embarquées) ; la vérification de bundle (npm run bundle:check) prouve qu'il
bundle avec les options du runtime et sans aucune API Node. La recette :
Ajouter un crew.
Vos propres crews et commandes, hors du checkout
Le REPL accepte --crews-dir et --commands-dir plusieurs fois, et le lanceur transmet ce qu'il ne reconnaît
pas :
./orkeon-code -- --crews-dir ~/my-crews --commands-dir ~/my-commands
Un crew nommé x est <dir>/x/crew.ork.ts ; le premier dossier qui l'a gagne, et ceux du checkout passent en
premier. Le runtime monte chaque dossier en lecture seule (/crews, /crews-1, ... ; /cli-commands,
/cli-commands-1, ...). Un crew hors du checkout importe le code partagé par un chemin relatif vers le checkout. Cela
fonctionne en modes tool et source ; en mode docker, le conteneur ne voit que le src du checkout et le projet, et
les fichiers vont donc dans le checkout.
Un outil
En TypeScript, dans un crew. toolBuilder() déclare un nom, une description, un schéma JSON et un execute qui
s'exécute dans le script ; .withAutonomousTool(tool) sur l'agent le confie au modèle, et ctx.llm.act le propose
comme un outil intégré. Le catalogue de permissions de main-loop liste les outils de l'hôte par leur nom et ignore
tout d'un outil construit : décidez dans le crew, d'après le mode, de l'attacher ou non.
const wordCount = toolBuilder<{ text: string }, { words: number }>()
.name("word_count")
.description("Count the words of a text.")
.withSchema({ type: "object", properties: { text: { type: "string" } }, required: ["text"] })
.execute((input) => ({ words: input.text.trim().split(/\s+/).length }))
.build();
Un outil du runtime. Vérifiez le nom que l'hôte enregistre (/debug-tool-call list), puis ajoutez-le à sa classe
dans src/shared/domain/agent-policy/permission.ts (READ_TOOLS, NETWORK_READ_TOOLS, ou les classes d'édition et
de shell) : la table des politiques de mode décide quels modes le confient au modèle. Quand du code applicatif
l'appelle, l'appel passe par un port et un adaptateur de src/shared/infrastructure/orkeon/. La recette :
Ajouter un outil.
Sur l'hôte. L'outil shell exécute les commandes de sa liste blanche : Orkeon:Tools:Shell:ExtraAllowedCommands
ajoute les outils de build de vos projets, AllowInterpreters ouvre node, dotnet, npm...
(Configuration). Un outil écrit en C# est un plugin Orkeon (un assembly qui implémente
IOrkeonPlugin) ; un outil servi par un serveur MCP ne demande aucun code. Le REPL connecte les serveurs de
MCP:Servers et charge les plugins de Plugins, et leurs outils rejoignent le catalogue de l'assistant dès que la clé
de session tools.extra les nomme avec leur classe (<name>:read|write)
(Configuration).
Prompts et comportement
Chaque prompt est écrit d'après une spécification de comportement,
Architecture, dans les mots propres de ce projet
(ADR 0003, en anglais) : changez d'abord la spécification,
puis le texte, puis les phrases-marqueurs que son test épingle. node scripts/render-prompts.mjs <dir> écrit chaque
prompt tel que le modèle le lit, et un changement se relit donc comme le diff de deux rendus.
- La boucle principale.
src/crews/main-loop/domain/prompt-assembly.tsconstruit l'en-tête statique (règles de comportement, outils et ce qu'ils savent faire, guide de recherche, fichier d'instructions du projet) et le corps utilisateur (préférences, skills, mémoire, hooks, environnement, historique, jauge, requête) : une fonction de rendubuild<Name>Blockpar bloc, un champ dePromptParts, une place dans l'ordre fixe d'assemblePrompt. L'entrée d'un bloc est lue dansapplication/context-gather.ts, par les ports, et passée dansapplication/turn-prompt.ts; les réglages qu'une session fixe (/effort,/passesde/review,/fast,/proactive,/output-style,answer.language) voyagent de la même façon. - Compaction et mémoire.
src/shared/domain/compaction.tstient les prompts de résumé et d'extraction de mémoire et les marqueurs que le modèle relit ensuite dans son historique ;src/shared/application/compaction/les exécute. - Les crews. Les prompts de chaque crew sont à côté de son cas d'usage (
application/prompts.ts, ou son module de domaine) et ont leurs propres tests.
npm run check:provenance refuse les marqueurs du matériau d'un autre produit, et tests/repository/prompt-contract.test.ts
vérifie chaque prompt rendu. La recette : Ajouter un bloc de prompt.
Modes de permission
Un mode s'énonce une fois : une entrée de PERMISSION_MODES (src/shared/domain/permission-modes.ts) et une ligne de
MODE_POLICIES (src/shared/domain/agent-policy/mode-policy.ts) : lecture seule ou non, et les classes d'outils
qu'il propose, les classes étant les listes d'agent-policy/permission.ts. Déplacez un outil d'une classe à une autre
et chaque mode suit ; le contrôle de types refuse un mode sans sa ligne ; les tests épinglent la promesse de
lecture seule de plan et de dontAsk. Les cinq noms de modes sont aussi connus de la garde de l'hôte, qui traite un
nom inconnu comme default (fail-closed) : changer ce qu'un mode propose est un changement TypeScript, ajouter un
sixième mode est aussi un changement du runtime. Les règles de la session, sur un outil ou sur ses arguments
(/permissions add allow shell_command command=npm test*), sont celles de la garde de l'hôte et ne demandent aucun
code. La recette : Ajouter un mode de permission.
Modèles et settings
Tout point d'accès compatible OpenAI, choisi dans le fichier de settings (Llm:BaseUrl, Llm:Model) ; /model
bascule parmi Llm:AvailableModels sans relancer ; les variables d'environnement ORKEON_* l'emportent sur n'importe
quelle clé (Configuration). Une nouvelle clé de session est une ligne de l'espace de clés déclaré
(Ajouter une clé de configuration).
Les limites
- Les scripts s'exécutent dans Jint, sur un seul thread, sans API Node ni timers :
src/n'importe rien denode:*(la vérification de bundle le refuse), et une commande ne fait jamais elle-même un long travail, elle le confie à un crew. - Le code applicatif ne touche jamais le runtime : il reçoit des ports, et
tests/repository/architecture.test.tsfait échouer tout import, et tout usage d'une globale du runtime, qui casse la règle de dépendance. .tools([...])nomme des outils de l'hôte, et un nom inconnu fait échouer chaque appelactde l'agent avant que le modèle soit sollicité : un outil qui manque au runtime ne peut pas être ajouté d'ici./debug-tool-call listmontre ce que l'hôte propose.- Un résultat d'outil arrive au modèle tronqué à 4000 caractères, avec une coupe plus large pour certains outils
(
Orkeon:Tools:ResultLimits, Configuration). - Les hooks déclarés (
/hooks) s'exécutent autour des appels d'outils des étapes dev, pas dans/assistant, et aucun hookstopne s'exécute (Hooks). Les agents sauvegardés (/agents) sont stockés, pas exécutés : la délégation qui les utiliserait est sur la Feuille de route.