HN 日本語サマリー

← 一覧へ戻る
プログラミング

Jevの実践:型付けされた決定、スコープ化された権限

Jev in practice: typed decisions, scoped authority (tenuo.ai)

23 pointsby niyikiza4 コメント

要約

この記事では、Tenuoが開発したJev、LangGraph、Tenuoを組み合わせた安全なエージェント委任システム「safe-upgrade」について解説しています。Jevはリポジトリの証拠を型付けされた確率的決定に変換し、LangGraphがその決定をワークフローで実行、Tenuoが各アクションにタスクスコープの権限(ワラント)を付与します。この設計により、判断、制御フロー、権限を分離し、依存関係のアップグレードのような複雑なタスクを安全かつ効率的に実行します。

全文翻訳

Jevの実践: 型付けされた決定、スコープ化された権限

Tenuo Engineering 2026年9月21日

Jev、LangGraph、Tenuoを用いた安全なエージェント委任の構築

TL;DR: safe-upgrade は、Jev、LangGraph、Tenuoを基盤とした依存関係アップグレードエージェントです。Jevはリポジトリの証拠を型付けされた確率的決定に変換します。LangGraphはその決定を耐久性のあるワークフローで伝達します。Tenuoは結果として生じる各アクションにタスクスコープの権限を付与します。設計原則は、判断、制御フロー、権限を分離することです。

[画像: コンセプトフロー: リポジトリの証拠がJevで型付けされた決定、LangGraphでワークフロー状態、Tenuoでスコープ化された権限を経てアクションが実行される。]

Jev、LangGraph、Tenuoがシステムの異なる部分をそれぞれ担当する場合、有用なワークフローはどのようなものになるかという、ビルダーからの問いから始めました。

依存関係のアップグレードは、それを実証する実用的な方法を提供しました。なぜなら、リポジトリのメンテナーはすぐに学ぶように、一見機械的なプロセスでも、困難な互換性の問題を隠している可能性があるからです。バージョンを変更し、パッケージをインストールし、テストを実行し、プルリクエストを開いても、リリースによってリポジトリが呼び出すAPIが削除されたり、モジュール形式が変更されたり、既存のスイートが実行しない動作が変更されたりする可能性があります。インストールとCIは成功しても、アプリケーションは壊れたままかもしれません。

有用なアップグレードエージェントは、リリース証拠を解釈し、それを未知のリポジトリに関連付け、必要な作業を決定し、コードを変更し、結果が健全であることを確立する必要があります。また、ソースファイル、パッケージインストール、テストコマンド、Git、そして時にはリモートリポジトリへのアクセスも受け取ります。

この組み合わせにより、エージェント開発の両側面、つまり、あいまいなコンテキストから有用な決定を下すことと、それに続くアクションを制御することの両方を研究することができました。

プログラムが使用できる決定から始める

依存関係のアップグレードには、通常のルールではうまく処理できない意味論的な問題が含まれるため、Jevを起点としました。

  • このリリースノートは、このリポジトリがパッケージを使用する方法に影響しますか?
  • 既存のテストは報告された破損を検出しますか?
  • 現在適格などのステップが最もリスクを軽減しますか?
  • 最終的なパッチは、各移行の発見事項に対処していますか?

Jev は、開発者が状態と型付けされた質問を提供する、ソフトウェア内の決定のためのSystem One Modelです。Choice の場合、開発者は利用可能なオプションを定義し、Jevは選択されたオプション、確率分布、およびプログラムがルーティングに使用できる信頼度を返します。

safe-upgrade では、決定論的なコードが最初に適格なアクションとその理由を計算します。その後、プロダクションアダプターがこのTypeSafe SDK呼び出しを行います。

import { choice, TypeSafeClient } from "@typesafe-ai/sdk";

const client = new TypeSafeClient();
const criteria = Object.fromEntries(
  input.eligibleActions.map(({ action, reason }) => [action, reason]),
);

const result = await client.systemOne({
  state: JSON.parse(JSON.stringify(input)),
  questions: {
    next: choice(
      "Choose the eligible action that most directly reduces the unresolved " +
        "risk in this upgrade. Every option is already permitted; pick the " +
        "most useful one.",
      criteria,
    ),
  },
});

const answer = result.answers.next;

実際のChoiceレスポンスは、この型付けされた形状を持ちます。以下の値は、回帰テストの追加が最も強力な次のステップであった実行例を示しています。

{
  "type": "choice",
  "choice": "author_tests",
  "confidence": 0.82,
  "probabilities": {
    "author_tests": 0.74,
    "assess_verification": 0.18,
    "implement": 0.08
  }
}

ルーターは、選択されたラベルを元の候補と比較して検証し、信頼度を通常のTypeScriptで使用します。

const choice = validateRouteChoice(
  {
    action: answer.choice,
    confidence: answer.confidence,
    probabilities: answer.probabilities,
  },
  eligibleActions.map(({ action }) => action),
);

const selected = choice.confidence >= confidenceThreshold
  ? choice
  : deterministicFallback(input);

const worker = ACTION_WORKER[selected.action];

Jevは、リッチなリポジトリ状態がリッチなままであり、結果が小さな型付けされた決定としてグラフに入るため、ここで特に役立ちます。低い信頼度、不正な形式のレスポンス、またはトランスポートの失敗は、ワークフローを決定論的なルートに送ります。候補セットはリクエストの前に確立され、ランタイムは返された選択を後で再度検証します。

Jevの決定からTenuoの権限へ

Jevのレスポンスは、選択されたアクションと信頼度で終了します。実行は、信頼されたルーティングコードがそのアクションを既知のワーカーにマッピングするときに開始されます。これにより、次のアーキテクチャ上の質問が生じます。そのワーカーはこの呼び出しに対してどのような権限を持つべきか?

author_tests の場合、答えはリポジトリの読み取りツール、テストファイルの書き込み、およびテスト実行です。本番環境では、Tenuoは明示的に信頼された発行者の公開鍵で初期化され、次に外部で発行された実行権限とその所有鍵の証明書をインポートします。抜粋は、そのインポートステップから始まります。ローカルデモでは、開発ルートを使用し、コマンド出力でそのモードを示します。

ワーカーは、実行の親権限から狭められた、短命のターミナルセッションを受け取ります。

const parentSession = tenuo.sessionFromWire({
  warrant: process.env.TENUO_RUN_WARRANT!,
  holderKey: createTenuo.holderKeyFromEnv(
    "TENUO_RUN_HOLDER_SECRET",
  ),
});

const testAuthor = tighten(
  pick(ceilings, [
    ...READ_ONLY,
    "write_test_file",
    "run_check",
  ]),
  "run_check",
  "kind",
  oneOf(["test"]),
);

const childSession = tenuo.narrow(parentSession, testAuthor, {
  terminal: true,
  ttlSeconds: 600,
});

親権限は、実行の最大権限を設定します。狭めると、より小さなツールセットが選択され、run_check がテストに絞り込まれ、有効期間が短縮され、子ターミナルになるため、再度委任できなくなります。

実装者は、ソース変更と正確な依存関係の更新のために別のチャイルドセッションを受け取りますが、write_test_file はそのプロファイルから除外されます。実装コードが回帰テストを弱めようとすると、書き込みツールよりも前に承認が実行されます。

await broker.withWorker("implementer", "implement", (handle) =>
  handle.tools.write_test_file({
    path: "src/sneaky.test.ts",
    expectedBeforeHash: "absent",
    content: "test.skip('regression', () => {});",
  }),
);

テストされた拒否記録が始まります。

TENUO_TOOL_NOT_AUTHORIZED
worker: implementer
capability: write_test_file

承認テストは、基盤となる書き込み関数が実際には実行されないことも確認します。拒否記録には、ワーカー、機能、引数ハッシュ、セッション識別子が含まれ、試行されたファイルの内容は省略されます。これにより、グラフ内の役割は強制的な実行境界に変わります。テスト作成者はテストを書き、実装者はソースを書き、検証者は候補ファイルを読み取り専用で保持してチェックを実行します。

LangGraphが結果をワークフローで伝達する

LangGraphは、実行全体のリポジトリの事実、発見事項、決定、試行、および証拠を保持します。各ノードは明示的な状態を受け取り更新を返し、チェックポイントは意味のある境界で進捗を保存します。

repository evidence
       |
       v
trusted code computes eligible transitions
       |
       v
Jev selects within that typed set
       |
       v
trusted code maps the action to a worker
       |
       v
Tenuo narrows a warrant for the worker
       |
       v
protected tools return evidence to LangGraph

評価は読み取り専用のワーカーのみを使用し、影響を受けるファイル、移行の発見事項、現在のチェック、および検証のギャップを記録します。評価の適用は、フォーカスされたテストを追加したり、ソースを移行したり、正確な依存関係を更新したり、一時的なGitワークツリーで候補を検証したりすることができます。評価は、それが検査したコミット、マニフェスト、ロックファイル、および作業ツリーの状態にバインドされます。

CIでこれが重要な理由

エージェントがGitHub Actionsで無人で実行される場合、同じ境界がより重要になります。CIジョブには、リポジトリのチェックアウト、パッケージマネージャーのキャッシュ、GitHubトークン、およびプルリクエストへのコメントまたはオープン権限が含まれる場合があります。ジョブレベルの権限は、ワークフローIDが何に到達できるかを確立します。これらは、この特定の実行が特定のパッケージ、ブランチ、またはファイルを変更する可能性がある理由について、詳細を提供しません。

safe-upgrade は、イベントからDependabotプルリクエストを評価したり、検証済みのアップグレードをドラフトとして開いたりできます。本番環境では、CIプロセスは信頼されたルートによって発行された実行権限をインポートします。その権限は、実行を要求されたパッケージと正確なバージョン、一時的な作業ツリー、1つのブランチ、およびドラフトのみの公開にバインドできます。各グラフノードは、その上限からより狭いチャイルド権限を受け取ります。

これにより、パイプラインは2つのレベルの制御を得ます。

  • GitHub Actionsの権限は、評価ジョブの contents: read や pull-requests: write のようなワークフローIDを制限します。
  • Tenuoの権限は、許可されたツール、引数、パス、パッケージバージョン、ブランチ、および有効期間を含む、各委任タスクを制限します。

コードを変更したりリポジトリの認証情報を使用したりできるCIエージェントにとって、この区別は重要です。ワークフローIDは、どのプリンシパルが実行されているかに答えます。タスクレベルの権限は、委任された各ステップを通じて特定のアップグレードの権限を伝達します。

ここでの「安全」の意味

アーキテクチャは、検査可能なプロパティを通じて安全性を定義します。

  • クローズドな決定: Jevは、信頼されたコードが既に適格と判断した遷移の中から選択します。
  • スコープ化された実行: すべてのワーカーは、ツールと引数のための短命の権限を受け取ります。
  • 分離された職務: テスト作成、実装、検証、公開は、異なる書き込み権限を使用します。
  • 独立した証拠: 検証はすべての発見事項を評価しますが、候補ファイルは読み取り専用のままです。
  • 封じ込められたプロセス: Tenuoは保護されたツール呼び出しを承認し、オペレーティングシステムのサンドボックスはリポジトリおよびパッケージプロセスが何にアクセスできるかを制限します。
  • 明示的な不確実性: 低い信頼度、古い状態、ポリシー違反、および不完全な証拠は、資格のある、または停止した結果を生じさせます。

Jevは依然としてリリース文を誤読する可能性があり、リポジトリチェックは重要な動作を省略する可能性があり、信頼されたラッパーは欠陥のあるポリシーをエンコードする可能性があります。狭い質問、信頼度を意識したルーティング、スコープ化された権限、および独立した証拠は、それらの不確実性を可視化し、それらに続くアクションを制限します。

再利用可能なパターン

このパターンは、依存関係のアップグレードを超えて拡張されます。

  1. 信頼された状態から有効な遷移を計算します。
  2. それらの遷移について、Jevに境界のある意味論的な質問をします。
  3. 型付けされたレスポンスを検証し、既知のワーカーにマッピングします。
  4. ワーカーのタスクに狭められたTenuo権限を委任します。
  5. LangGraphに証拠を返し、独立して検証します。

スタックは1つの原則に基づいています。

アクションを推奨するコンポーネントと、その権限を定義するコンポーネントを分離してください。

実装は safe-upgrade リポジトリ で探索するか、npmから実行できます。

npx @tenuo/safe-upgrade doctor
npx @tenuo/safe-upgrade assess --repository .

ここで示された実装は、@typesafe-ai/sdk 0.6.0 および @tenuo/core 0.3.0-beta.0 に対して構築されました。また、完全な承認ワークフローをライブJev APIに対して検証し、リポジトリにはエンドツーエンドのスモークテストが含まれています。

Jevのリクエストとレスポンスモデルは、TypeSafeクイックスタート で文書化されており、TenuoはGitHub で利用可能です。


safe-upgrade は、Jev、LangGraph、Tenuoで構築されたオープンソースの依存関係アップグレードエージェントです。ソースコードと実行可能な例は、safe-upgrade リポジトリ で入手できます。

Case study DelegationAI agent securityJevLangGraphTypeScript