Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 28 additions & 13 deletions src-tauri/crates/agent-cli/src/session_provenance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,14 @@ const CLAUDE_CODE_LIFECYCLE_EVENTS: &[(&str, Option<&str>)] = &[
("PreToolUse", Some("*")),
("PostToolUseFailure", Some("*")),
];
// Codex lifecycle events (all matcher-less; SessionStart doubles as the
// resume signal and PreToolUse is the per-tool working heartbeat).
// Codex events required whenever provenance capture is enabled. SessionStart
// proves that Codex accepted and executed the current managed definitions;
// the subagent events preserve exact actor attribution.
const CODEX_REQUIRED_EVENTS: &[&str] = &["SessionStart", "SubagentStart", "SubagentStop"];
// Optional Codex lifecycle events (all matcher-less). SessionStart remains
// installed when live status is off because it also drives hook activation;
// PreToolUse is the per-tool working heartbeat when live status is on.
const CODEX_LIFECYCLE_EVENTS: &[&str] = &[
"SessionStart",
"UserPromptSubmit",
"PreToolUse",
"PermissionRequest",
Expand Down Expand Up @@ -358,7 +362,7 @@ pub struct SessionProvenanceHookStatus {
#[serde(rename_all = "snake_case")]
pub enum SessionProvenanceHookActivationState {
Inactive,
AwaitingApproval,
AwaitingVerification,
Active,
}

Expand Down Expand Up @@ -597,7 +601,7 @@ fn update_codex_platform(
unix_command,
windows_command,
)?;
for event_name in ["SubagentStart", "SubagentStop"] {
for event_name in CODEX_REQUIRED_EVENTS {
update_nested_event(
config,
event_name,
Expand Down Expand Up @@ -1473,8 +1477,9 @@ fn config_has_complete_managed_hooks(
platform,
"PostToolUse",
Some(CODEX_POST_TOOL_USE_MATCHER),
) && nested_event_has_managed_hook(config, platform, "SubagentStart", None)
&& nested_event_has_managed_hook(config, platform, "SubagentStop", None)
) && CODEX_REQUIRED_EVENTS
.iter()
.all(|event_name| nested_event_has_managed_hook(config, platform, event_name, None))
&& (!live_status
|| CODEX_LIFECYCLE_EVENTS.iter().all(|event_name| {
nested_event_has_managed_hook(config, platform, event_name, None)
Expand Down Expand Up @@ -1642,7 +1647,10 @@ fn codex_activation_from_receipt(
Some(receipt.activated_at),
)
} else {
(SessionProvenanceHookActivationState::AwaitingApproval, None)
(
SessionProvenanceHookActivationState::AwaitingVerification,
None,
)
}
}

Expand Down Expand Up @@ -1709,7 +1717,7 @@ fn build_hook_status(

/// Record proof that Codex invoked the current ORG2-managed hook definition.
/// A matching receipt is the only state that upgrades the UI from
/// `awaiting_approval` to `active`.
/// `awaiting_verification` to `active`.
pub fn record_session_provenance_hook_activation(source: &str) -> Result<bool, String> {
if source != SessionProvenanceHookPlatform::Codex.source_arg() {
return Ok(false);
Expand Down Expand Up @@ -2057,7 +2065,7 @@ mod tests {
}

#[test]
fn codex_config_installs_and_removes_actor_lifecycle_hooks() {
fn codex_config_installs_and_removes_required_hooks() {
let mut config = json!({
"hooks": {
"SubagentStop": [{
Expand All @@ -2076,6 +2084,7 @@ mod tests {
.expect("enable Codex hooks");

assert_eq!(config["hooks"]["PostToolUse"].as_array().unwrap().len(), 1);
assert_eq!(config["hooks"]["SessionStart"].as_array().unwrap().len(), 1);
assert_eq!(
config["hooks"]["SubagentStart"].as_array().unwrap().len(),
1
Expand All @@ -2087,7 +2096,7 @@ mod tests {
false
));

config["hooks"]["SubagentStart"] = json!([]);
config["hooks"]["SessionStart"] = json!([]);
assert!(!config_has_complete_managed_hooks(
&config,
SessionProvenanceHookPlatform::Codex,
Expand Down Expand Up @@ -2169,11 +2178,17 @@ mod tests {
};
assert_eq!(
codex_activation_from_receipt("current", None),
(SessionProvenanceHookActivationState::AwaitingApproval, None)
(
SessionProvenanceHookActivationState::AwaitingVerification,
None
)
);
assert_eq!(
codex_activation_from_receipt("stale", Some(receipt.clone())),
(SessionProvenanceHookActivationState::AwaitingApproval, None)
(
SessionProvenanceHookActivationState::AwaitingVerification,
None
)
);
assert_eq!(
codex_activation_from_receipt("current", Some(receipt)),
Expand Down
4 changes: 2 additions & 2 deletions src/api/tauri/rpc/__tests__/sessionProvenanceSchemas.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ describe("session provenance RPC schemas", () => {
platform: "codex",
enabled: true,
desiredEnabled: true,
activationState: "awaiting_approval",
activationState: "awaiting_verification",
lastActivatedAt: null,
configPath: "/Users/test/.codex/hooks.json",
error: null,
});
expect(parsed.enabled).toBe(true);
expect(parsed.activationState).toBe("awaiting_approval");
expect(parsed.activationState).toBe("awaiting_verification");
});

it("parses a recent hook signal from the Rust camelCase payload", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/api/tauri/rpc/schemas/agentOrgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const SessionProvenanceHookPlatformSchema = z.enum([

export const SessionProvenanceHookActivationStateSchema = z.enum([
"inactive",
"awaiting_approval",
"awaiting_verification",
"active",
]);

Expand Down
14 changes: 8 additions & 6 deletions src/i18n/locales/de/integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1688,6 +1688,8 @@
"emptyConfigPreview": "Kein Konfigurationsinhalt"
},
"sessionProvenance": {
"masterToggle": "Provenienz-Hooks",
"masterToggleDesc": "Wenn deaktiviert, werden alle verwalteten Hooks deinstalliert und keine Signale erfasst; die Auswahl pro Plattform bleibt erhalten und wird beim erneuten Aktivieren wiederhergestellt.",
"title": "Sitzungsherkunft",
"capture": "Dateiinteraktionen erfassen",
"description": "Erfasst Lese- und Schreibzugriffe auf Dateien als Metadaten. Prompts, Werkzeugausgaben und Dateiinhalte werden nicht gespeichert.",
Expand All @@ -1704,16 +1706,16 @@
"on": "Ein",
"off": "Aus",
"repair": "Reparieren",
"needsApproval": "Genehmigung erforderlich",
"awaitingVerification": "Warten auf Verifizierung",
"checking": "Wird geprüft…",
"error": "Fehler"
},
"codexApproval": {
"title": "Approve ORG2 hooks in Codex",
"description": "Codex has the ORG2 hooks installed, but Codex still needs your approval before it can run them.",
"instructions": "Open Codex, review the 3 ORG2 hooks, then choose Trust all and continue. ORG2 marks this active after the first real hook signal.",
"review": "Review in Codex",
"verified": "Verified by a real Codex hook signal {{time}}."
"title": "ORG2-Hooks in Codex verifizieren",
"description": "Es wird darauf gewartet, dass Codex die aktuellen ORG2-Hooks genehmigt und ausführt.",
"instructions": "Öffne Codex, prüfe die ORG2-Hooks und wähle dann Trust all and continue. Der SessionStart-Hook bestätigt die Aktivierung automatisch, sobald die Sitzung startet.",
"review": "In Codex prüfen",
"verified": "Durch ein echtes Codex-Hook-Signal verifiziert ({{time}})."
},
"signals": {
"title": "Aktuelle Signale",
Expand Down
8 changes: 4 additions & 4 deletions src/i18n/locales/en/integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -2400,14 +2400,14 @@
"on": "On",
"off": "Off",
"repair": "Repair",
"needsApproval": "Needs approval",
"awaitingVerification": "Awaiting verification",
"checking": "Checking…",
"error": "Error"
},
"codexApproval": {
"title": "Approve ORG2 hooks in Codex",
"description": "Codex has the ORG2 hooks installed, but Codex still needs your approval before it can run them.",
"instructions": "Open Codex, review the 3 ORG2 hooks, then choose Trust all and continue. ORG2 marks this active after the first real hook signal.",
"title": "Verify ORG2 hooks in Codex",
"description": "Waiting for Codex to approve and execute the current ORG2 hooks.",
"instructions": "Open Codex, review the ORG2 hooks, then choose Trust all and continue. The SessionStart hook verifies activation automatically when the session starts.",
"review": "Review in Codex",
"verified": "Verified by a real Codex hook signal {{time}}."
},
Expand Down
14 changes: 8 additions & 6 deletions src/i18n/locales/es/integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1685,6 +1685,8 @@
"emptyConfigPreview": "Sin contenido de configuración"
},
"sessionProvenance": {
"masterToggle": "Hooks de procedencia",
"masterToggleDesc": "Al desactivarlos, se desinstalan todos los hooks administrados y no se captura ninguna señal; las selecciones por plataforma se conservan y se restauran al volver a activarlos.",
"title": "Procedencia de sesiones",
"capture": "Registrar interacciones con archivos",
"description": "Registra lecturas y escrituras de archivos como metadatos. No se guardan prompts, salidas de herramientas ni contenido de archivos.",
Expand All @@ -1701,16 +1703,16 @@
"on": "Activado",
"off": "Desactivado",
"repair": "Reparar",
"needsApproval": "Requiere aprobación",
"awaitingVerification": "Esperando verificación",
"checking": "Comprobando…",
"error": "Error"
},
"codexApproval": {
"title": "Approve ORG2 hooks in Codex",
"description": "Codex has the ORG2 hooks installed, but Codex still needs your approval before it can run them.",
"instructions": "Open Codex, review the 3 ORG2 hooks, then choose Trust all and continue. ORG2 marks this active after the first real hook signal.",
"review": "Review in Codex",
"verified": "Verified by a real Codex hook signal {{time}}."
"title": "Verificar los hooks de ORG2 en Codex",
"description": "Esperando a que Codex apruebe y ejecute los hooks actuales de ORG2.",
"instructions": "Abre Codex, revisa los hooks de ORG2 y elige Trust all and continue. El hook SessionStart verifica la activación automáticamente al iniciar la sesión.",
"review": "Revisar en Codex",
"verified": "Verificado mediante una señal real de un hook de Codex ({{time}})."
},
"signals": {
"title": "Señales recientes",
Expand Down
14 changes: 8 additions & 6 deletions src/i18n/locales/fr/integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1688,6 +1688,8 @@
"emptyConfigPreview": "Aucun contenu de configuration"
},
"sessionProvenance": {
"masterToggle": "Hooks de provenance",
"masterToggleDesc": "Lorsque cette option est désactivée, tous les hooks gérés sont désinstallés et aucun signal n’est collecté ; les choix par plateforme sont conservés et restaurés à la réactivation.",
"title": "Traçabilité des sessions",
"capture": "Enregistrer les interactions avec les fichiers",
"description": "Enregistre les lectures et écritures de fichiers sous forme de métadonnées. Les prompts, les sorties d’outils et le contenu des fichiers ne sont pas stockés.",
Expand All @@ -1704,16 +1706,16 @@
"on": "Activé",
"off": "Désactivé",
"repair": "Réparer",
"needsApproval": "Approbation requise",
"awaitingVerification": "En attente de vérification",
"checking": "Vérification…",
"error": "Erreur"
},
"codexApproval": {
"title": "Approve ORG2 hooks in Codex",
"description": "Codex has the ORG2 hooks installed, but Codex still needs your approval before it can run them.",
"instructions": "Open Codex, review the 3 ORG2 hooks, then choose Trust all and continue. ORG2 marks this active after the first real hook signal.",
"review": "Review in Codex",
"verified": "Verified by a real Codex hook signal {{time}}."
"title": "Vérifier les hooks ORG2 dans Codex",
"description": "En attente de l’approbation et de l’exécution des hooks ORG2 actuels par Codex.",
"instructions": "Ouvrez Codex, vérifiez les hooks ORG2, puis choisissez Trust all and continue. Le hook SessionStart vérifie automatiquement l’activation au démarrage de la session.",
"review": "Vérifier dans Codex",
"verified": "Vérifié par un signal réel de hook Codex ({{time}})."
},
"signals": {
"title": "Signaux récents",
Expand Down
14 changes: 8 additions & 6 deletions src/i18n/locales/ja/integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1688,6 +1688,8 @@
"emptyConfigPreview": "設定内容がありません"
},
"sessionProvenance": {
"masterToggle": "プロベナンス hooks",
"masterToggleDesc": "オフにすると、管理対象の hooks がすべてアンインストールされ、シグナルは取得されません。プラットフォームごとの選択は保持され、再度オンにすると復元されます。",
"title": "セッションの来歴",
"capture": "ファイル操作を記録",
"description": "ファイルの読み取りと書き込みをメタデータとして記録します。プロンプト、ツール出力、ファイル内容は保存されません。",
Expand All @@ -1704,16 +1706,16 @@
"on": "オン",
"off": "オフ",
"repair": "修復",
"needsApproval": "承認が必要",
"awaitingVerification": "検証待ち",
"checking": "確認中…",
"error": "エラー"
},
"codexApproval": {
"title": "Approve ORG2 hooks in Codex",
"description": "Codex has the ORG2 hooks installed, but Codex still needs your approval before it can run them.",
"instructions": "Open Codex, review the 3 ORG2 hooks, then choose Trust all and continue. ORG2 marks this active after the first real hook signal.",
"review": "Review in Codex",
"verified": "Verified by a real Codex hook signal {{time}}."
"title": "Codex で ORG2 hooks を検証",
"description": "Codex が現在の ORG2 hooks を承認して実行するのを待っています。",
"instructions": "Codex を開いて ORG2 hooks を確認し、Trust all and continue を選択してください。セッションの開始時に SessionStart hook が有効化を自動検証します。",
"review": "Codex で確認",
"verified": "実際の Codex hook シグナルで検証済み({{time}})。"
},
"signals": {
"title": "最近のシグナル",
Expand Down
14 changes: 8 additions & 6 deletions src/i18n/locales/ko/integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1685,6 +1685,8 @@
"emptyConfigPreview": "설정 내용이 없습니다"
},
"sessionProvenance": {
"masterToggle": "출처 추적 hooks",
"masterToggleDesc": "끄면 관리되는 모든 hooks가 제거되고 신호를 수집하지 않습니다. 플랫폼별 선택은 유지되며 다시 켜면 복원됩니다.",
"title": "세션 출처",
"capture": "파일 상호작용 기록",
"description": "파일 읽기와 쓰기를 메타데이터로 기록합니다. 프롬프트, 도구 출력 및 파일 내용은 저장하지 않습니다.",
Expand All @@ -1701,16 +1703,16 @@
"on": "켜짐",
"off": "꺼짐",
"repair": "복구",
"needsApproval": "승인 필요",
"awaitingVerification": "확인 대기 중",
"checking": "확인 중…",
"error": "오류"
},
"codexApproval": {
"title": "Approve ORG2 hooks in Codex",
"description": "Codex has the ORG2 hooks installed, but Codex still needs your approval before it can run them.",
"instructions": "Open Codex, review the 3 ORG2 hooks, then choose Trust all and continue. ORG2 marks this active after the first real hook signal.",
"review": "Review in Codex",
"verified": "Verified by a real Codex hook signal {{time}}."
"title": "Codex에서 ORG2 hooks 확인",
"description": "Codex가 현재 ORG2 hooks를 승인하고 실행하기를 기다리는 중입니다.",
"instructions": "Codex를 열고 ORG2 hooks를 검토한 다음 Trust all and continue를 선택하세요. 세션이 시작되면 SessionStart hook이 활성화를 자동으로 확인합니다.",
"review": "Codex에서 검토",
"verified": "실제 Codex hook 신호로 확인됨({{time}})."
},
"signals": {
"title": "최근 신호",
Expand Down
14 changes: 8 additions & 6 deletions src/i18n/locales/pl/integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1685,6 +1685,8 @@
"emptyConfigPreview": "Brak treści konfiguracji"
},
"sessionProvenance": {
"masterToggle": "Hooki pochodzenia",
"masterToggleDesc": "Po wyłączeniu wszystkie zarządzane hooki zostaną odinstalowane i żadne sygnały nie będą zbierane; wybory dla poszczególnych platform zostaną zachowane i przywrócone po ponownym włączeniu.",
"title": "Pochodzenie sesji",
"capture": "Rejestruj interakcje z plikami",
"description": "Rejestruje odczyty i zapisy plików jako metadane. Prompty, wyniki narzędzi i zawartość plików nie są przechowywane.",
Expand All @@ -1701,16 +1703,16 @@
"on": "Wł.",
"off": "Wył.",
"repair": "Napraw",
"needsApproval": "Wymaga zatwierdzenia",
"awaitingVerification": "Oczekiwanie na weryfikację",
"checking": "Sprawdzanie…",
"error": "Błąd"
},
"codexApproval": {
"title": "Approve ORG2 hooks in Codex",
"description": "Codex has the ORG2 hooks installed, but Codex still needs your approval before it can run them.",
"instructions": "Open Codex, review the 3 ORG2 hooks, then choose Trust all and continue. ORG2 marks this active after the first real hook signal.",
"review": "Review in Codex",
"verified": "Verified by a real Codex hook signal {{time}}."
"title": "Weryfikacja hooków ORG2 w Codex",
"description": "Oczekiwanie, aż Codex zatwierdzi i uruchomi bieżące hooki ORG2.",
"instructions": "Otwórz Codex, sprawdź hooki ORG2, a następnie wybierz Trust all and continue. Hook SessionStart automatycznie zweryfikuje aktywację po rozpoczęciu sesji.",
"review": "Sprawdź w Codex",
"verified": "Zweryfikowano prawdziwym sygnałem hooka Codex ({{time}})."
},
"signals": {
"title": "Ostatnie sygnały",
Expand Down
14 changes: 8 additions & 6 deletions src/i18n/locales/pt/integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1688,6 +1688,8 @@
"emptyConfigPreview": "Sem conteúdo de configuração"
},
"sessionProvenance": {
"masterToggle": "Hooks de proveniência",
"masterToggleDesc": "Quando desativado, todos os hooks gerenciados são desinstalados e nenhum sinal é capturado; as escolhas de cada plataforma são mantidas e restauradas ao reativar.",
"title": "Proveniência da sessão",
"capture": "Registrar interações com arquivos",
"description": "Registra leituras e gravações de arquivos como metadados. Prompts, saídas de ferramentas e conteúdo dos arquivos não são armazenados.",
Expand All @@ -1704,16 +1706,16 @@
"on": "Ativado",
"off": "Desativado",
"repair": "Reparar",
"needsApproval": "Requer aprovação",
"awaitingVerification": "Aguardando verificação",
"checking": "Verificando…",
"error": "Erro"
},
"codexApproval": {
"title": "Approve ORG2 hooks in Codex",
"description": "Codex has the ORG2 hooks installed, but Codex still needs your approval before it can run them.",
"instructions": "Open Codex, review the 3 ORG2 hooks, then choose Trust all and continue. ORG2 marks this active after the first real hook signal.",
"review": "Review in Codex",
"verified": "Verified by a real Codex hook signal {{time}}."
"title": "Verificar hooks do ORG2 no Codex",
"description": "Aguardando o Codex aprovar e executar os hooks atuais do ORG2.",
"instructions": "Abra o Codex, revise os hooks do ORG2 e escolha Trust all and continue. O hook SessionStart verifica a ativação automaticamente quando a sessão começa.",
"review": "Revisar no Codex",
"verified": "Verificado por um sinal real de hook do Codex ({{time}})."
},
"signals": {
"title": "Sinais recentes",
Expand Down
Loading