Files
hapi/ios/Packages/HapiKit/Sources/HapiProtocol/Models/AgentState.swift
T
weishu 0c4abcb3d1 feat(codex): share sessions across terminal and web
Use one native app-server for terminal, Web and phone clients while retaining the existing CLI and Runner lifecycle.

Synchronize native queues, permissions, question history and steering state; preserve explicit permission precedence and per-turn usage models. Resume inactive clear commands through Runner and reject independent child cold resumes.

Add shared-runtime regression tests, generated protocol fixtures and lifecycle documentation.
2026-09-12 10:59:17 +08:00

115 lines
4.0 KiB
Swift

import Foundation
/// A pending tool-approval / user-input request raised by the agent.
///
/// Mirrors `AgentStateRequestSchema` (`shared/src/schemas.ts`). Permission
/// requests are NOT messages: they live on `Session.agentState.requests`
/// keyed by request id and are resolved over
/// `POST /api/sessions/:id/permissions/:requestId/approve|deny`.
public struct AgentStateRequest: Codable, Equatable, Sendable {
public var tool: String
/// Correlates a native tool call; approval still uses the request dictionary key.
public var toolCallId: String?
/// Free-form tool arguments (`z.unknown()` on the wire).
public var arguments: JSONValue?
/// Epoch ms when the request was raised; older CLIs omit it.
public var createdAt: Int?
public init(tool: String, arguments: JSONValue? = nil, createdAt: Int? = nil, toolCallId: String? = nil) {
self.tool = tool
self.toolCallId = toolCallId
self.arguments = arguments
self.createdAt = createdAt
}
}
/// A resolved permission request, moved from `requests` to
/// `completedRequests` once decided.
///
/// Mirrors `AgentStateCompletedRequestSchema` (`shared/src/schemas.ts`).
public struct AgentStateCompletedRequest: Codable, Equatable, Sendable {
public enum Status: String, Codable, Sendable {
case resolved
case canceled
case denied
case approved
}
public enum Decision: String, Codable, Sendable {
case approved
case approvedForSession = "approved_for_session"
case denied
case abort
}
public var tool: String
/// Correlates a native tool call; approval still uses the request dictionary key.
public var toolCallId: String?
public var arguments: JSONValue?
public var createdAt: Int?
public var completedAt: Int?
public var status: Status
public var reason: String?
/// Permission mode switched to while approving, when any.
public var mode: String?
public var decision: Decision?
public var allowTools: [String]?
/// Two wire formats, kept raw:
/// flat `{question: [answers]}` (AskUserQuestion) or
/// nested `{question: {answers: [...]}}` (request_user_input).
public var answers: JSONValue?
public init(
tool: String,
arguments: JSONValue? = nil,
createdAt: Int? = nil,
completedAt: Int? = nil,
status: Status,
reason: String? = nil,
mode: String? = nil,
decision: Decision? = nil,
allowTools: [String]? = nil,
answers: JSONValue? = nil,
toolCallId: String? = nil
) {
self.tool = tool
self.toolCallId = toolCallId
self.arguments = arguments
self.createdAt = createdAt
self.completedAt = completedAt
self.status = status
self.reason = reason
self.mode = mode
self.decision = decision
self.allowTools = allowTools
self.answers = answers
}
}
/// Agent-owned session state carried on `Session.agentState` and patched over
/// SSE under the `agentStateVersion` gate.
///
/// Mirrors `AgentStateSchema` (`shared/src/schemas.ts:195-203`).
public struct AgentState: Codable, Equatable, Sendable {
/// `true` while the terminal owns the session (remote control paused).
public var controlledByUser: Bool?
/// Launch mode the session was started in (persisted for reopen/resume).
public var startingMode: SessionStartingMode?
/// Pending requests keyed by request id.
public var requests: [String: AgentStateRequest]?
/// Resolved requests keyed by request id.
public var completedRequests: [String: AgentStateCompletedRequest]?
public init(
controlledByUser: Bool? = nil,
startingMode: SessionStartingMode? = nil,
requests: [String: AgentStateRequest]? = nil,
completedRequests: [String: AgentStateCompletedRequest]? = nil
) {
self.controlledByUser = controlledByUser
self.startingMode = startingMode
self.requests = requests
self.completedRequests = completedRequests
}
}