Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
8df9b58
style(local-service): clarify backend go comments
Blackcloudss May 9, 2026
a5ce9f1
ci(local-service): block chinese go comments
Blackcloudss May 9, 2026
c26057b
chore(local-service): merge upstream main
Blackcloudss May 9, 2026
acf5b2a
refactor(orchestrator): stage task entry flows
Blackcloudss May 9, 2026
9b25743
refactor(rpc): align entrypoint decoding with protocol
Blackcloudss May 9, 2026
50c6d22
test(rpc): guard protocol method registry
Blackcloudss May 9, 2026
42cfff7
refactor(orchestrator): type task entry DTOs
Blackcloudss May 9, 2026
935ce8f
refactor(orchestrator): type screen approval state
Blackcloudss May 9, 2026
ee7af3b
chore(repo): merge upstream main
Blackcloudss May 9, 2026
2ad95a0
fix(local-service): enforce typed rpc dto boundaries
xgopilot May 10, 2026
a2fb7c2
fix(local-service): enforce typed rpc request meta
xgopilot May 10, 2026
a407c85
fix(local-service): enforce stable rpc request meta
xgopilot May 10, 2026
ac4eb30
fix(local-service): normalize typed dto boundaries
xgopilot May 10, 2026
a098f6a
test(local-service): align rpc tests with latest main
xgopilot May 10, 2026
22af073
Merge pull request #58 from Blackcloudss/fork-pr-497-1778378524
Blackcloudss May 10, 2026
1de2feb
fix(local-service): tighten typed entry dto contracts
xgopilot May 10, 2026
83a559c
Merge pull request #59 from Blackcloudss/fork-pr-497-1778378524
Blackcloudss May 10, 2026
ffac556
chore(repo): merge upstream main after pr 497
Blackcloudss May 10, 2026
e2b561e
fix(rpc): remove unused typed dto test helpers
xgopilot May 10, 2026
a24afd1
fix(rpc): tighten stable task list and task start params
xgopilot May 10, 2026
0217e6c
Merge pull request #62 from Blackcloudss/fork-pr-497-1778393362
Blackcloudss May 10, 2026
8ecd1f5
Merge branch 'main' into codex/issues-440-442-typed-boundaries
Blackcloudss May 10, 2026
d7e8cbe
fix(context): drop retired task entry aliases
xgopilot May 10, 2026
cd8faaa
chore(repo): merge upstream main after pr 497 refresh
Blackcloudss May 10, 2026
c53e4d2
fix(local-service): align typed dto boundaries
xgopilot May 10, 2026
cbcb59c
Merge pull request #66 from Blackcloudss/fork-pr-497-1778402071
Blackcloudss May 10, 2026
ea8732e
fix(rpc): accept typed dto normalized values
xgopilot May 10, 2026
4f9403d
Merge pull request #67 from Blackcloudss/fork-pr-497-1778402071
Blackcloudss May 10, 2026
1663b60
docs(protocol): align task start typed context examples
xgopilot May 10, 2026
1d8ad02
Merge pull request #68 from Blackcloudss/fork-pr-497-1778402071
Blackcloudss May 10, 2026
b5ce50d
Merge remote-tracking branch 'upstream/main' into codex/issues-440-44…
Blackcloudss May 10, 2026
16cbfdb
fix(orchestrator): build response dtos directly
Blackcloudss May 10, 2026
db0afd4
test(orchestrator): use typed task detail helper
Blackcloudss May 10, 2026
b85acbd
test(rpc): include metadata in delivery open dispatch
Blackcloudss May 10, 2026
c758544
fix(local-service): tighten typed dto boundaries
xgopilot May 10, 2026
c27ce88
Merge pull request #73 from Blackcloudss/fork-pr-497-1778418967
Blackcloudss May 10, 2026
e8954d3
docs(protocol): unify shared input context contract
xgopilot May 10, 2026
3673d9c
Merge pull request #75 from Blackcloudss/fork-pr-497-1778418967
Blackcloudss May 10, 2026
d3a6b04
fix(local-service): harden typed dto boundary validation
xgopilot May 10, 2026
3f3c3ab
Merge pull request #78 from Blackcloudss/fork-pr-497-1778418967
Blackcloudss May 10, 2026
b8b5c78
refactor(local-service): stop hand-assembling typed dto maps
xgopilot May 10, 2026
83a7f97
Merge pull request #79 from Blackcloudss/fork-pr-497-1778418967
Blackcloudss May 10, 2026
f622124
test(local-service): use typed task entry helpers
xgopilot May 10, 2026
b0a9ea8
Merge pull request #80 from Blackcloudss/fork-pr-497-1778418967
Blackcloudss May 10, 2026
c7c2118
test(local-service): make typed entry helpers explicit
xgopilot May 10, 2026
7f28b4b
Merge pull request #82 from Blackcloudss/fork-pr-497-1778418967
Blackcloudss May 10, 2026
974823b
test(orchestrator): use typed task entry requests
xgopilot May 10, 2026
1ed33f4
Merge pull request #83 from Blackcloudss/fork-pr-497-1778433504
Blackcloudss May 10, 2026
8edcfaf
Merge remote-tracking branch 'upstream/main' into codex/issues-440-44…
Blackcloudss May 10, 2026
c94cb2a
fix(orchestrator): keep runtime approval entry responses valid
Blackcloudss May 10, 2026
8d9783b
fix(orchestrator): build task detail dto directly
Blackcloudss May 10, 2026
97e8d86
fix(orchestrator): remove stale task detail map helpers
Blackcloudss May 10, 2026
827e841
fix(local-service): tighten task entry dto boundaries
xgopilot May 11, 2026
4bdaa6c
Merge pull request #90 from Blackcloudss/fork-pr-497-1778459732
Blackcloudss May 11, 2026
86909d5
fix(orchestrator): drop dead delivery helper
xgopilot May 11, 2026
ef7a9f9
fix(local-service): restore intercepted fallback and drop malformed urls
xgopilot May 11, 2026
5577bb0
Merge pull request #93 from Blackcloudss/fork-pr-497-1778464944
Blackcloudss May 11, 2026
a3aeadd
fix(rpc): keep typed dto responses on transport
xgopilot May 11, 2026
e4dc891
Merge pull request #96 from Blackcloudss/fork-pr-497-1778464944
Blackcloudss May 11, 2026
b0260be
fix(local-service): sanitize nested opaque context urls
xgopilot May 11, 2026
d529ccc
Merge pull request #99 from Blackcloudss/fork-pr-497-1778464944
Blackcloudss May 11, 2026
2edaf8c
fix(local-service): tighten typed entry dto validation
xgopilot May 11, 2026
8242827
Merge pull request #101 from Blackcloudss/fork-pr-497-1778471603
Blackcloudss May 11, 2026
d5428f4
fix(rpc): require plugin request metadata
xgopilot May 11, 2026
690f3e5
Merge pull request #103 from Blackcloudss/fork-pr-497-1778471603
Blackcloudss May 11, 2026
b7603a6
Merge branch 'main' into codex/issues-440-442-typed-boundaries
Blackcloudss May 11, 2026
491c72f
fix(local-service): tighten typed session and intent contracts
xgopilot May 11, 2026
847bcd7
Merge pull request #106 from Blackcloudss/fork-pr-497-1778487074
Blackcloudss May 11, 2026
cd11f8c
merge(pr-497): integrate origin/main natural-language confirmation up…
xgopilot May 11, 2026
3c19971
fix(rpc): resolve notifications test merge conflict
xgopilot May 11, 2026
dd40517
Merge pull request #109 from Blackcloudss/fork-pr-497-1778506617
Blackcloudss May 11, 2026
729bf34
Merge branch 'main' into codex/issues-440-442-typed-boundaries
Blackcloudss May 11, 2026
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
2 changes: 1 addition & 1 deletion docs/module-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -1323,7 +1323,7 @@ flowchart TB

#### 上下文范围
- 请求入口上下文:`source / trigger / input_type / input_mode / text`
- 近场对象上下文:`selection_text / error_text / files`
- 近场对象上下文:`selection_text / error.message / files`
Comment thread
fennoai[bot] marked this conversation as resolved.
- 页面与窗口上下文:`page_title / page_url / app_name / window_title / visible_text`
- 屏幕与行为上下文:`screen_summary / hover_target / last_action / dwell_millis / copy_count / switch_count`
- 系统补充上下文:`clipboard_text`
Expand Down
72 changes: 49 additions & 23 deletions docs/protocol-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,49 @@ Notification 只负责“状态变化推送”,不承载复杂业务命令。

## 8.1 入口承接 / 语音 / 场景助手

### 8.1.0 共享 `InputContext` 上下文字段

`agent.input.submit` 与 `agent.task.start` 共享同一个稳定 `InputContext` 包。
两条入口若传入 `context`,其字段集合与语义必须保持一致,不允许只在其中一条入口单独扩字段。

| 字段 | 中文说明 |
| ---- | -------- |
| `context.page` | 当前页面上下文 |
| `context.page.title` | 当前页面标题 |
| `context.page.url` | 当前页面 URL |
| `context.page.browser_kind` | 当前浏览器分类,取值为 `chrome / edge / other_browser / non_browser` |
| `context.page.process_path` | 当前宿主进程路径 |
| `context.page.process_id` | 当前宿主进程 ID |
| `context.page.app_name` | 当前宿主应用名 |
| `context.page.window_title` | 当前窗口标题 |
| `context.page.visible_text` | 当前页面可见文本摘录 |
| `context.page.hover_target` | 当前悬停目标摘要,按需传入 |
| `context.screen.summary` | 当前屏幕摘要,可用于视觉型任务上下文 |
| `context.screen.screen_summary` | 当前屏幕摘要的结构化别名,按需传入 |
| `context.screen.visible_text` | 当前屏幕可见文本摘录 |
| `context.screen.window_title` | 当前屏幕对应窗口标题,按需传入 |
| `context.screen.hover_target` | 当前屏幕上的悬停目标摘要,按需传入 |
| `context.behavior.last_action` | 最近行为信号,例如 `copy` |
| `context.behavior.dwell_millis` | 当前场景停留时长 |
| `context.behavior.copy_count` | 最近复制次数,按需传入 |
| `context.behavior.window_switch_count` | 最近窗口切换次数,按需传入 |
| `context.behavior.page_switch_count` | 最近页面切换次数,按需传入 |
| `context.selection.text` | 当前选区补充文本,按需传入 |
| `context.error.message` | 当前错误文本,按需传入 |
| `context.clipboard.text` | 当前剪贴板文本,按需传入 |
| `context.text` | 当前自由文本补充,按需传入 |
| `context.selection_text` | 当前选区的平铺补充文本,按需传入 |
| `context.files` | 当前附带文件列表,按需传入 |
| `context.file_paths` | 兼容文件路径列表,按需传入 |
| `context.screen_summary` | 当前屏幕摘要的平铺别名,按需传入 |
| `context.clipboard_text` | 当前剪贴板文本的平铺别名,按需传入 |
| `context.hover_target` | 当前悬停目标摘要的平铺别名,按需传入 |
| `context.last_action` | 最近行为信号的平铺别名,按需传入 |
| `context.dwell_millis` | 当前场景停留时长的平铺别名,按需传入 |
| `context.copy_count` | 最近复制次数的平铺别名,按需传入 |
| `context.window_switch_count` | 最近窗口切换次数的平铺别名,按需传入 |
| `context.page_switch_count` | 最近页面切换次数的平铺别名,按需传入 |

### 8.1.1 `agent.input.submit`

- **请求方式**:JSON-RPC 2.0
Expand Down Expand Up @@ -617,21 +660,7 @@ Notification 只负责“状态变化推送”,不承载复杂业务命令。
| `input.type` | 输入对象类型,固定为 `text` |
| `input.text` | 用户输入文本 |
| `input.input_mode` | 输入模式,语音或文字 |
| `context.page` | 当前页面上下文 |
| `context.page.title` | 当前页面标题 |
| `context.page.url` | 当前页面 URL |
| `context.page.browser_kind` | 当前浏览器分类,取值为 `chrome / edge / other_browser / non_browser` |
| `context.page.process_path` | 当前宿主进程路径 |
| `context.page.process_id` | 当前宿主进程 ID |
| `context.page.app_name` | 当前宿主应用名 |
| `context.page.window_title` | 当前窗口标题 |
| `context.page.visible_text` | 当前页面可见文本摘录 |
| `context.selection.text` | 当前选中文本 |
| `context.files` | 当前附带文件列表 |
| `context.screen.summary` | 当前屏幕摘要,可用于视觉型任务上下文 |
| `context.screen.visible_text` | 当前屏幕可见文本摘录 |
| `context.behavior.last_action` | 最近行为信号,例如 `copy` |
| `context.behavior.dwell_millis` | 当前场景停留时长 |
| `context` | 共享上下文包,字段定义见上方共享 `InputContext` 表 |
| `voice_meta` | 语音会话元信息 |
| `options.confirm_required` | 是否强制先进入意图确认 |
| `options.preferred_delivery` | 偏好的结果交付方式 |
Expand Down Expand Up @@ -808,12 +837,7 @@ Notification 只负责“状态变化推送”,不承载复杂业务命令。
| `input.page_context.app_name` | 当前宿主应用名 |
| `input.page_context.window_title` | 当前窗口标题 |
| `input.page_context.visible_text` | 当前页面可见文本摘录 |
| `context.selection.text` | 当前选区补充文本,按需传入 |
| `context.files` | 补充文件上下文,按需传入 |
| `context.screen.summary` | 当前屏幕摘要,可用于视觉型任务上下文 |
| `context.screen.visible_text` | 当前屏幕可见文本摘录 |
| `context.behavior.last_action` | 最近行为信号,例如 `copy` |
| `context.behavior.dwell_millis` | 当前场景停留时长 |
| `context` | 共享上下文包,字段定义见上方共享 `InputContext` 表 |
| `delivery.preferred` | 优先交付方式 |
| `delivery.fallback` | 兜底交付方式 |
| `options.confirm_required` | 是否强制先进入意图确认;不用于绕过风险授权 |
Expand Down Expand Up @@ -855,14 +879,16 @@ Notification 只负责“状态变化推送”,不承载复杂业务命令。
"text": "这里放用户选中的文本内容",
"page_context": {
"app_name": "Chrome",
"url": "https://example.com/release",
"image_url": "xxx.png"
"url": "https://example.com/release"
}
},
"context": {
"selection": {
"text": "这里是补充上下文"
},
"error": {
"message": "页面上显示了一段失败日志"
},
"files": []
},
"delivery": {
Expand Down
6 changes: 3 additions & 3 deletions packages/protocol/rpc/methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -865,7 +865,7 @@ export interface AgentSettingsModelValidateResult {

// AgentPluginRuntimeListParams defines the stable plugin runtime query params.
export interface AgentPluginRuntimeListParams {
request_meta?: RequestMeta;
request_meta: RequestMeta;
}

// AgentPluginRuntimeListResult defines the plugin runtime query result.
Expand All @@ -876,7 +876,7 @@ export interface AgentPluginRuntimeListResult {
}

export interface AgentPluginListParams {
request_meta?: RequestMeta;
request_meta: RequestMeta;
page?: {
limit: number;
offset: number;
Expand All @@ -892,7 +892,7 @@ export interface AgentPluginListResult {
}

export interface AgentPluginDetailGetParams {
request_meta?: RequestMeta;
request_meta: RequestMeta;
plugin_id: string;
include_runtime?: boolean;
include_metrics?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,19 @@ func (s *Service) resumeQueuedControlledTask(task runengine.TaskRecord) (runengi
if stringValue(task.Intent, "name", "") != "screen_analyze" {
return task, false, nil
}
approvalRequest, pendingExecution, bubble, err := s.buildScreenAnalysisApprovalState(task)
approvalState, err := s.buildScreenAnalysisApprovalState(task)
if err != nil {
failedTask, _ := s.failExecutionTask(task, map[string]any{"name": "screen_analyze"}, execution.Result{}, err)
failedTask, _ := s.failExecutionTask(task, protocolIntentMap("screen_analyze", nil), execution.Result{}, err)
return failedTask, true, nil
}
approvalRequest := approvalState.approvalRequestMap()
pendingExecution := approvalState.pendingExecutionMap()
bubble := approvalState.bubbleMessageMap()
updatedTask, ok := s.runEngine.MarkWaitingApprovalWithPlan(task.TaskID, approvalRequest, pendingExecution, bubble)
if !ok {
return runengine.TaskRecord{}, true, ErrTaskNotFound
}
if err := s.persistApprovalRequestState(updatedTask.TaskID, approvalRequest, mapValue(pendingExecution, "impact_scope")); err != nil {
if err := s.persistApprovalRequestState(updatedTask.TaskID, approvalRequest, approvalState.PendingExecution.ImpactScope.mapValue()); err != nil {
return runengine.TaskRecord{}, true, err
}
return updatedTask, true, nil
Expand Down
43 changes: 19 additions & 24 deletions services/local-service/internal/orchestrator/governance.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,23 +81,23 @@ func (s *Service) assessTaskGovernance(task runengine.TaskRecord, taskIntent map
})
}

func (s *Service) handleTaskGovernanceDecision(task runengine.TaskRecord, taskIntent map[string]any) (runengine.TaskRecord, map[string]any, bool, error) {
func (s *Service) handleTaskGovernanceDecision(task runengine.TaskRecord, taskIntent map[string]any) (runengine.TaskRecord, TaskEntryResponse, bool, error) {
assessment, ok, err := s.assessTaskGovernance(task, taskIntent)
if err != nil {
return task, nil, false, err
return task, TaskEntryResponse{}, false, err
}
if !ok {
assessment, ok = s.fallbackGovernanceAssessment(task, taskIntent)
if !ok {
return task, nil, false, nil
return task, TaskEntryResponse{}, false, nil
}
}
if assessment.Deny {
response, blockedTask, blockErr := s.blockTaskByAssessment(task, assessment)
return blockedTask, response, true, blockErr
}
if !assessment.ApprovalRequired {
return task, nil, false, nil
return task, TaskEntryResponse{}, false, nil
}
pendingExecution := s.applyGovernanceAssessment(s.buildPendingExecution(task, taskIntent), assessment)
approvalRequest := buildApprovalRequest(task.TaskID, taskIntent, assessment)
Expand All @@ -110,16 +110,16 @@ func (s *Service) handleTaskGovernanceDecision(task runengine.TaskRecord, taskIn
updatedTask, changed = s.runEngine.MarkWaitingApprovalWithPlan(task.TaskID, approvalRequest, pendingExecution, bubble)
}
if !changed {
return task, nil, false, ErrTaskNotFound
return task, TaskEntryResponse{}, false, ErrTaskNotFound
}
if err := s.persistApprovalRequestState(updatedTask.TaskID, approvalRequest, assessment.ImpactScope); err != nil {
return task, nil, false, err
return task, TaskEntryResponse{}, false, err
}
return updatedTask, map[string]any{
"task": taskMap(updatedTask),
"bubble_message": bubble,
"delivery_result": nil,
}, true, nil
response, err := buildTaskEntryResponse(&updatedTask, bubble, nil)
if err != nil {
return task, TaskEntryResponse{}, false, err
}
return updatedTask, response, true, nil
}

func (s *Service) maybePauseForRuntimeApproval(task runengine.TaskRecord, taskIntent map[string]any, result execution.Result) (runengine.TaskRecord, map[string]any, bool, error) {
Expand Down Expand Up @@ -151,11 +151,7 @@ func (s *Service) maybePauseForRuntimeApproval(task runengine.TaskRecord, taskIn
if err := s.persistApprovalRequestState(updatedTask.TaskID, approvalRequest, assessment.ImpactScope); err != nil {
return task, nil, false, err
}
return updatedTask, map[string]any{
"task": taskMap(updatedTask),
"bubble_message": bubble,
"delivery_result": nil,
}, true, nil
return updatedTask, bubble, true, nil
}

// runtimeApprovalAssessment reconstructs the formal approval_request boundary
Expand Down Expand Up @@ -280,7 +276,7 @@ func (s *Service) fallbackGovernanceAssessment(task runengine.TaskRecord, taskIn
}, true
}

func (s *Service) blockTaskByAssessment(task runengine.TaskRecord, assessment execution.GovernanceAssessment) (map[string]any, runengine.TaskRecord, error) {
func (s *Service) blockTaskByAssessment(task runengine.TaskRecord, assessment execution.GovernanceAssessment) (TaskEntryResponse, runengine.TaskRecord, error) {
bubbleText := governanceInterceptionBubble(assessment)
bubble := s.delivery.BuildBubbleMessage(task.TaskID, "status", bubbleText, task.UpdatedAt.Format(dateTimeLayout))
updatedTask := runengine.TaskRecord{}
Expand All @@ -291,16 +287,15 @@ func (s *Service) blockTaskByAssessment(task runengine.TaskRecord, assessment ex
updatedTask, ok = s.runEngine.BlockTaskByPolicy(task.TaskID, assessment.RiskLevel, bubbleText, assessment.ImpactScope, bubble)
}
if !ok {
return nil, task, ErrTaskNotFound
return TaskEntryResponse{}, task, ErrTaskNotFound
}
auditRecord := s.writeGovernanceAuditRecord(updatedTask.TaskID, updatedTask.RunID, "risk", "intercept_operation", bubbleText, impactScopeTarget(assessment.ImpactScope, assessment.TargetObject), "denied")
updatedTask = s.appendAuditData(updatedTask, compactAuditRecords(auditRecord), nil)
return map[string]any{
"task": taskMap(updatedTask),
"bubble_message": bubble,
"delivery_result": nil,
"impact_scope": cloneMap(assessment.ImpactScope),
}, updatedTask, nil
response, err := buildTaskEntryResponse(&updatedTask, bubble, nil)
if err != nil {
return TaskEntryResponse{}, task, err
}
return response, updatedTask, nil
}

func (s *Service) writeGovernanceAuditRecord(taskID, runID, auditType, action, summary, target, result string) map[string]any {
Expand Down
55 changes: 35 additions & 20 deletions services/local-service/internal/orchestrator/input_submit.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,26 @@ import (
// SubmitInput adapts free-form user input into the task-centric execution path.
// It captures context, derives an intent suggestion, then either waits for more
// input, asks for confirmation, or creates the task/run pair for execution.
func (s *Service) SubmitInput(params map[string]any) (map[string]any, error) {
func (s *Service) SubmitInput(request SubmitInputRequest) (TaskEntryResponse, error) {
return s.SubmitInputFromParams(request.ProtocolParamsMap())
}

// SubmitInputFromParams lets the RPC layer reuse the normalized protocol map it
// already validated so submit-input requests avoid an extra DTO-to-map bounce.
func (s *Service) SubmitInputFromParams(params map[string]any) (TaskEntryResponse, error) {
return s.submitInput(params)
}

func (s *Service) submitInput(params map[string]any) (TaskEntryResponse, error) {
flow := s.prepareInputSubmitFlow(params)
if response, handled, err := s.maybeContinueInputSubmit(&flow); err != nil || handled {
return response, err
}
if response, handled, err := s.maybeHandleSuggestedInputScreen(flow); err != nil || handled {
return response, err
}
if response, handled := s.maybeRouteUnanchoredInput(&flow); handled {
return response, nil
if response, handled, err := s.maybeRouteUnanchoredInput(&flow); err != nil || handled {
return response, err
}
flow.PreferredDelivery, flow.FallbackDelivery = inputSubmitDeliveryPreference(flow)
if response, handled, err := s.maybeCreateWaitingInputTask(flow); err != nil || handled {
Expand All @@ -46,7 +56,7 @@ func (s *Service) prepareInputSubmitFlow(params map[string]any) taskEntryFlow {
}
}

func (s *Service) maybeContinueInputSubmit(flow *taskEntryFlow) (map[string]any, bool, error) {
func (s *Service) maybeContinueInputSubmit(flow *taskEntryFlow) (TaskEntryResponse, bool, error) {
response, handled, resolvedSessionID, err := s.maybeContinueExistingTask(flow.Params, flow.Snapshot, nil, taskContinuationOptions{
ConfirmRequired: flow.ConfirmRequired,
ForceConfirmRequired: flow.ForceConfirmRequired,
Expand All @@ -57,23 +67,27 @@ func (s *Service) maybeContinueInputSubmit(flow *taskEntryFlow) (map[string]any,
if strings.TrimSpace(resolvedSessionID) != "" {
flow.Params = withResolvedSessionID(flow.Params, resolvedSessionID)
}
return nil, false, nil
return TaskEntryResponse{}, false, nil
}

func (s *Service) maybeHandleSuggestedInputScreen(flow taskEntryFlow) (map[string]any, bool, error) {
func (s *Service) maybeHandleSuggestedInputScreen(flow taskEntryFlow) (TaskEntryResponse, bool, error) {
return s.handleScreenAnalyzeSuggestion(flow.Params, flow.Snapshot, flow.Suggestion)
}

func (s *Service) maybeRouteUnanchoredInput(flow *taskEntryFlow) (map[string]any, bool) {
func (s *Service) maybeRouteUnanchoredInput(flow *taskEntryFlow) (TaskEntryResponse, bool, error) {
decision, ok := s.routeUnanchoredSubmitInput(context.Background(), flow.Snapshot, flow.Suggestion, flow.ConfirmRequired)
if !ok {
return nil, false
return TaskEntryResponse{}, false, nil
}
if decision.Route == inputRouteSocialChat {
return s.socialChatInputResponse(decision), true
response, err := s.socialChatInputResponse(decision)
if err != nil {
return TaskEntryResponse{}, false, err
}
return response, true, nil
}
flow.Suggestion = applyInputRouteDecision(flow.Suggestion, decision)
return nil, false
return TaskEntryResponse{}, false, nil
}

func inputSubmitDeliveryPreference(flow taskEntryFlow) (string, string) {
Expand All @@ -84,9 +98,9 @@ func inputSubmitDeliveryPreference(flow taskEntryFlow) (string, string) {
return preferredDelivery, fallbackDelivery
}

func (s *Service) maybeCreateWaitingInputTask(flow taskEntryFlow) (map[string]any, bool, error) {
func (s *Service) maybeCreateWaitingInputTask(flow taskEntryFlow) (TaskEntryResponse, bool, error) {
if s.intent.AnalyzeSnapshot(flow.Snapshot) != "waiting_input" {
return nil, false, nil
return TaskEntryResponse{}, false, nil
}
task := s.runEngine.CreateTask(runengine.CreateTaskInput{
SessionID: stringValue(flow.Params, "session_id", ""),
Expand All @@ -106,24 +120,25 @@ func (s *Service) maybeCreateWaitingInputTask(flow taskEntryFlow) (map[string]an

bubble := s.delivery.BuildBubbleMessage(task.TaskID, "status", presentation.Text(presentation.MessageBubbleInputNeedGoal, nil), task.StartedAt.Format(dateTimeLayout))
task = s.persistTaskPresentation(task, bubble)
return buildTaskEntryResponse(task, bubble, nil), true, nil
response, err := buildTaskEntryResponse(&task, bubble, nil)
return response, true, err
}

func (s *Service) finishInputSubmit(flow taskEntryFlow, task runengine.TaskRecord) (map[string]any, error) {
func (s *Service) finishInputSubmit(flow taskEntryFlow, task runengine.TaskRecord) (TaskEntryResponse, error) {
bubble := s.delivery.BuildBubbleMessage(task.TaskID, bubbleTypeForSuggestion(flow.Suggestion.RequiresConfirm), bubbleTextForInput(flow.Suggestion), task.StartedAt.Format(dateTimeLayout))
if flow.Suggestion.RequiresConfirm {
task = s.persistTaskPresentation(task, bubble)
return buildTaskEntryResponse(task, bubble, nil), nil
return buildTaskEntryResponse(&task, bubble, nil)
}
if queuedTask, queueBubble, queued, queueErr := s.queueTaskIfSessionBusy(task); queueErr != nil {
return nil, queueErr
return TaskEntryResponse{}, queueErr
} else if queued {
return buildTaskEntryResponse(queuedTask, queueBubble, nil), nil
return buildTaskEntryResponse(&queuedTask, queueBubble, nil)
}

governedTask, governedResponse, handled, governanceErr := s.handleTaskGovernanceDecision(task, flow.Suggestion.Intent)
if governanceErr != nil {
return nil, governanceErr
return TaskEntryResponse{}, governanceErr
}
if handled {
return governedResponse, nil
Expand All @@ -134,9 +149,9 @@ func (s *Service) finishInputSubmit(flow taskEntryFlow, task runengine.TaskRecor
var execErr error
task, bubble, deliveryResult, _, execErr = s.executeTask(task, flow.Snapshot, flow.Suggestion.Intent)
if execErr != nil {
return nil, execErr
return TaskEntryResponse{}, execErr
}
return buildTaskEntryResponse(task, bubble, deliveryResult), nil
return buildTaskEntryResponse(&task, bubble, deliveryResult)
}

// deliveryPreferenceFromSubmit reads delivery preferences from
Expand Down
Loading
Loading