@pushpalsdev/cli 1.0.17 → 1.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pushpals-cli.js +542 -23
- package/package.json +1 -1
- package/runtime/sandbox/apps/workerpals/.python-version +1 -0
- package/runtime/sandbox/apps/workerpals/Dockerfile.sandbox +71 -0
- package/runtime/sandbox/apps/workerpals/package.json +25 -0
- package/runtime/sandbox/apps/workerpals/pyproject.toml +8 -0
- package/runtime/sandbox/apps/workerpals/src/backends/backend_config.ts +111 -0
- package/runtime/sandbox/apps/workerpals/src/backends/miniswe/miniswe_executor.py +2029 -0
- package/runtime/sandbox/apps/workerpals/src/backends/miniswe_backend.ts +48 -0
- package/runtime/sandbox/apps/workerpals/src/backends/openai_codex/openai_codex_executor.py +1259 -0
- package/runtime/sandbox/apps/workerpals/src/backends/openai_codex/test_openai_codex_runtime_config.py +110 -0
- package/runtime/sandbox/apps/workerpals/src/backends/openai_codex_backend.ts +67 -0
- package/runtime/sandbox/apps/workerpals/src/backends/openhands/openhands_executor.py +563 -0
- package/runtime/sandbox/apps/workerpals/src/backends/openhands_backend.ts +161 -0
- package/runtime/sandbox/apps/workerpals/src/backends/openhands_task_execute.ts +536 -0
- package/runtime/sandbox/apps/workerpals/src/backends/shared/executor_base.py +746 -0
- package/runtime/sandbox/apps/workerpals/src/backends/shared/test_settings_resolver.py +60 -0
- package/runtime/sandbox/apps/workerpals/src/backends/task_execute_registry.ts +21 -0
- package/runtime/sandbox/apps/workerpals/src/backends/types.ts +52 -0
- package/runtime/sandbox/apps/workerpals/src/common/execution_utils.ts +149 -0
- package/runtime/sandbox/apps/workerpals/src/common/executor_backend.ts +15 -0
- package/runtime/sandbox/apps/workerpals/src/common/generic_python_executor.ts +210 -0
- package/runtime/sandbox/apps/workerpals/src/common/logger.ts +65 -0
- package/runtime/sandbox/apps/workerpals/src/common/types.ts +9 -0
- package/runtime/sandbox/apps/workerpals/src/common/worktree_cleanup.ts +66 -0
- package/runtime/sandbox/apps/workerpals/src/context_manager.ts +45 -0
- package/runtime/sandbox/apps/workerpals/src/docker_executor.ts +1842 -0
- package/runtime/sandbox/apps/workerpals/src/execute_job.ts +3063 -0
- package/runtime/sandbox/apps/workerpals/src/job_runner.ts +194 -0
- package/runtime/sandbox/apps/workerpals/src/shell_manager.ts +210 -0
- package/runtime/sandbox/apps/workerpals/src/timeout_policy.ts +24 -0
- package/runtime/sandbox/apps/workerpals/src/workerpals_main.ts +1436 -0
- package/runtime/sandbox/apps/workerpals/tsconfig.json +15 -0
- package/runtime/sandbox/apps/workerpals/uv.lock +2014 -0
- package/runtime/sandbox/bun.lock +2591 -0
- package/runtime/sandbox/configs/backend.toml +79 -0
- package/runtime/sandbox/configs/default.toml +260 -0
- package/runtime/sandbox/configs/dev.toml +2 -0
- package/runtime/sandbox/configs/local.example.toml +129 -0
- package/runtime/sandbox/package.json +65 -0
- package/runtime/sandbox/packages/protocol/README.md +168 -0
- package/runtime/sandbox/packages/protocol/package.json +37 -0
- package/runtime/sandbox/packages/protocol/scripts/copy-schemas.js +17 -0
- package/runtime/sandbox/packages/protocol/src/a2a/README.md +52 -0
- package/runtime/sandbox/packages/protocol/src/a2a/mapping.ts +55 -0
- package/runtime/sandbox/packages/protocol/src/index.browser.ts +25 -0
- package/runtime/sandbox/packages/protocol/src/index.ts +25 -0
- package/runtime/sandbox/packages/protocol/src/schemas/approvals.schema.json +6 -0
- package/runtime/sandbox/packages/protocol/src/schemas/envelope.schema.json +96 -0
- package/runtime/sandbox/packages/protocol/src/schemas/events.schema.json +679 -0
- package/runtime/sandbox/packages/protocol/src/schemas/http.schema.json +50 -0
- package/runtime/sandbox/packages/protocol/src/types.ts +267 -0
- package/runtime/sandbox/packages/protocol/src/validate.browser.ts +154 -0
- package/runtime/sandbox/packages/protocol/src/validate.ts +233 -0
- package/runtime/sandbox/packages/protocol/src/version.ts +1 -0
- package/runtime/sandbox/packages/protocol/tsconfig.json +20 -0
- package/runtime/sandbox/packages/shared/package.json +19 -0
- package/runtime/sandbox/packages/shared/src/autonomy_policy.ts +400 -0
- package/runtime/sandbox/packages/shared/src/client_preflight.ts +297 -0
- package/runtime/sandbox/packages/shared/src/communication.ts +313 -0
- package/runtime/sandbox/packages/shared/src/config.ts +2201 -0
- package/runtime/sandbox/packages/shared/src/config_template_parity.ts +70 -0
- package/runtime/sandbox/packages/shared/src/git_backend.ts +205 -0
- package/runtime/sandbox/packages/shared/src/index.ts +100 -0
- package/runtime/sandbox/packages/shared/src/local_network.ts +101 -0
- package/runtime/sandbox/packages/shared/src/localbuddy_runtime.ts +329 -0
- package/runtime/sandbox/packages/shared/src/prompts.ts +64 -0
- package/runtime/sandbox/packages/shared/src/repo.ts +134 -0
- package/runtime/sandbox/packages/shared/src/session_event_visibility.ts +25 -0
- package/runtime/sandbox/packages/shared/src/vision.ts +247 -0
- package/runtime/sandbox/packages/shared/tsconfig.json +16 -0
- package/runtime/sandbox/prompts/workerpals/codex_quality_critic_instruction_prompt.md +14 -0
- package/runtime/sandbox/prompts/workerpals/commit_message_prompt.md +36 -0
- package/runtime/sandbox/prompts/workerpals/commit_message_user_prompt.md +7 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_broker_system_prompt.md +33 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_broker_task_prompt.md +5 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_completion_requirement.md +1 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_context_compaction_retry_prompt.md +1 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_explicit_targets_block.md +2 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_recovery_guidance_base.md +4 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_recovery_guidance_blocker_line.md +1 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_strict_tool_use_guidance.md +6 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_supplemental_guidance_section.md +2 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_timeout_note.md +1 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_toolcall_retry_guidance.md +1 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_default_system_prompt.md +4 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_instruction_wrapper.md +5 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_runtime_policy_appendix.md +5 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_supplemental_guidance_section.md +2 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_task_execute_system_prompt.md +12 -0
- package/runtime/sandbox/prompts/workerpals/openhands_minimal_security_policy.j2 +8 -0
- package/runtime/sandbox/prompts/workerpals/openhands_minimal_system_prompt.j2 +20 -0
- package/runtime/sandbox/prompts/workerpals/openhands_strict_tool_use_message.md +1 -0
- package/runtime/sandbox/prompts/workerpals/openhands_supplemental_guidance_message.md +2 -0
- package/runtime/sandbox/prompts/workerpals/openhands_task_execute_fallback_system_prompt.md +1 -0
- package/runtime/sandbox/prompts/workerpals/openhands_task_execute_system_prompt.md +21 -0
- package/runtime/sandbox/prompts/workerpals/openhands_task_user_prompt.md +6 -0
- package/runtime/sandbox/prompts/workerpals/openhands_timeout_note.md +1 -0
- package/runtime/sandbox/prompts/workerpals/pr_description.md +42 -0
- package/runtime/sandbox/prompts/workerpals/task_quality_critic_system_prompt.md +9 -0
- package/runtime/sandbox/prompts/workerpals/task_quality_critic_user_prompt.md +17 -0
- package/runtime/sandbox/prompts/workerpals/workerpals_system_prompt.md +115 -0
- package/runtime/sandbox/protocol/schemas/approvals.schema.json +6 -0
- package/runtime/sandbox/protocol/schemas/envelope.schema.json +96 -0
- package/runtime/sandbox/protocol/schemas/events.schema.json +679 -0
- package/runtime/sandbox/protocol/schemas/http.schema.json +50 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "HTTPContracts",
|
|
4
|
+
"definitions": {
|
|
5
|
+
"CreateSessionResponse": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["sessionId", "protocolVersion"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"sessionId": { "type": "string" },
|
|
10
|
+
"protocolVersion": { "type": "string", "const": "0.1.0" }
|
|
11
|
+
},
|
|
12
|
+
"additionalProperties": false
|
|
13
|
+
},
|
|
14
|
+
"MessageRequest": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"required": ["text"],
|
|
17
|
+
"properties": {
|
|
18
|
+
"text": { "type": "string" }
|
|
19
|
+
},
|
|
20
|
+
"additionalProperties": false
|
|
21
|
+
},
|
|
22
|
+
"MessageResponse": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"required": ["ok"],
|
|
25
|
+
"properties": {
|
|
26
|
+
"ok": { "type": "boolean" }
|
|
27
|
+
},
|
|
28
|
+
"additionalProperties": false
|
|
29
|
+
},
|
|
30
|
+
"ApprovalDecisionRequest": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"required": ["decision"],
|
|
33
|
+
"properties": {
|
|
34
|
+
"decision": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"enum": ["approve", "deny"]
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"additionalProperties": false
|
|
40
|
+
},
|
|
41
|
+
"ApprovalDecisionResponse": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"required": ["ok"],
|
|
44
|
+
"properties": {
|
|
45
|
+
"ok": { "type": "boolean" }
|
|
46
|
+
},
|
|
47
|
+
"additionalProperties": false
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { PROTOCOL_VERSION } from "./version";
|
|
2
|
+
|
|
3
|
+
// ─── Artifact type (reused across several payloads) ─────────────────────────
|
|
4
|
+
export interface Artifact {
|
|
5
|
+
kind: string;
|
|
6
|
+
uri?: string;
|
|
7
|
+
text?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Payload types for each event type - enables type narrowing
|
|
12
|
+
*/
|
|
13
|
+
export interface EventTypePayloadMap {
|
|
14
|
+
// ── Existing events ───────────────────────────────────────────────────────
|
|
15
|
+
log: { level: "debug" | "info" | "warn" | "error"; message: string };
|
|
16
|
+
scan_result: {
|
|
17
|
+
summary: string;
|
|
18
|
+
filesRead: string[];
|
|
19
|
+
gitStatusPorcelain: string;
|
|
20
|
+
gitDiff: string;
|
|
21
|
+
};
|
|
22
|
+
suggestions: {
|
|
23
|
+
items: Array<{ id: string; title: string; detail: string; effort: "S" | "M" | "L" }>;
|
|
24
|
+
};
|
|
25
|
+
diff_ready: { unifiedDiff: string; diffStat: string; branch: string };
|
|
26
|
+
approval_required: {
|
|
27
|
+
approvalId: string;
|
|
28
|
+
action: "git.commit" | "git.push" | "other";
|
|
29
|
+
summary: string;
|
|
30
|
+
details: Record<string, unknown>;
|
|
31
|
+
};
|
|
32
|
+
approved: { approvalId: string };
|
|
33
|
+
denied: { approvalId: string };
|
|
34
|
+
committed: { branch: string; commitHash: string; message: string };
|
|
35
|
+
assistant_message: { text: string };
|
|
36
|
+
error: { message: string; detail?: string };
|
|
37
|
+
done: { ok: boolean };
|
|
38
|
+
|
|
39
|
+
// ── Multi-agent events ────────────────────────────────────────────────────
|
|
40
|
+
agent_status: {
|
|
41
|
+
agentId: string;
|
|
42
|
+
status: "idle" | "busy" | "error";
|
|
43
|
+
message?: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// ── Task lifecycle events ─────────────────────────────────────────────────
|
|
47
|
+
task_created: {
|
|
48
|
+
taskId: string;
|
|
49
|
+
title: string;
|
|
50
|
+
description: string;
|
|
51
|
+
createdBy: string;
|
|
52
|
+
priority?: string;
|
|
53
|
+
tags?: string[];
|
|
54
|
+
};
|
|
55
|
+
task_started: { taskId: string };
|
|
56
|
+
task_progress: { taskId: string; message: string; percent?: number };
|
|
57
|
+
task_completed: {
|
|
58
|
+
taskId: string;
|
|
59
|
+
summary: string;
|
|
60
|
+
artifacts?: Artifact[];
|
|
61
|
+
};
|
|
62
|
+
task_failed: { taskId: string; message: string; detail?: string };
|
|
63
|
+
|
|
64
|
+
// ── Tool call/result events ───────────────────────────────────────────────
|
|
65
|
+
tool_call: {
|
|
66
|
+
toolCallId: string;
|
|
67
|
+
taskId?: string;
|
|
68
|
+
tool: string;
|
|
69
|
+
args: Record<string, unknown>;
|
|
70
|
+
requiresApproval?: boolean;
|
|
71
|
+
};
|
|
72
|
+
tool_result: {
|
|
73
|
+
toolCallId: string;
|
|
74
|
+
taskId?: string;
|
|
75
|
+
ok: boolean;
|
|
76
|
+
stdout?: string;
|
|
77
|
+
stderr?: string;
|
|
78
|
+
exitCode?: number;
|
|
79
|
+
artifacts?: Artifact[];
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// ── Delegation events ─────────────────────────────────────────────────────
|
|
83
|
+
delegate_request: {
|
|
84
|
+
requestId: string;
|
|
85
|
+
toAgentId: string;
|
|
86
|
+
input: Record<string, unknown>;
|
|
87
|
+
};
|
|
88
|
+
delegate_response: {
|
|
89
|
+
requestId: string;
|
|
90
|
+
ok: boolean;
|
|
91
|
+
output?: Record<string, unknown>;
|
|
92
|
+
error?: string;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// ── Job queue events ──────────────────────────────────────────────────────
|
|
96
|
+
job_enqueued: {
|
|
97
|
+
jobId: string;
|
|
98
|
+
taskId: string;
|
|
99
|
+
kind: string;
|
|
100
|
+
params: Record<string, unknown>;
|
|
101
|
+
origin?: "user" | "autonomy";
|
|
102
|
+
autonomy?: {
|
|
103
|
+
objectiveId?: string;
|
|
104
|
+
runId?: string;
|
|
105
|
+
snapshotId?: string;
|
|
106
|
+
patternKey?: string;
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
job_claimed: { jobId: string; workerId: string };
|
|
110
|
+
job_completed: {
|
|
111
|
+
jobId: string;
|
|
112
|
+
summary?: string;
|
|
113
|
+
artifacts?: Artifact[];
|
|
114
|
+
};
|
|
115
|
+
job_failed: { jobId: string; message: string; detail?: string };
|
|
116
|
+
// ── Streaming / lifecycle events ──────────────────────────────────────
|
|
117
|
+
/** User-facing chat message (from client to server) */
|
|
118
|
+
message: { text: string; intent?: Record<string, unknown> };
|
|
119
|
+
|
|
120
|
+
/** Streaming log line from a worker executing a job */
|
|
121
|
+
job_log: {
|
|
122
|
+
jobId: string;
|
|
123
|
+
stream: "stdout" | "stderr";
|
|
124
|
+
seq: number;
|
|
125
|
+
line: string;
|
|
126
|
+
ts?: string;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/** System heartbeat / status beacon */
|
|
130
|
+
status: {
|
|
131
|
+
agentId: string;
|
|
132
|
+
state: "idle" | "busy" | "error" | "shutting_down";
|
|
133
|
+
uptimeMs?: number;
|
|
134
|
+
detail?: string;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// —— Autonomous system lifecycle events ————————————————————————————————————————————
|
|
138
|
+
autonomy_cycle_started: {
|
|
139
|
+
runId: string;
|
|
140
|
+
snapshotId: string;
|
|
141
|
+
phase?: string;
|
|
142
|
+
};
|
|
143
|
+
autonomy_candidates_generated: {
|
|
144
|
+
runId: string;
|
|
145
|
+
snapshotId: string;
|
|
146
|
+
candidateCount: number;
|
|
147
|
+
topCandidateIds?: string[];
|
|
148
|
+
};
|
|
149
|
+
autonomy_objective_dispatched: {
|
|
150
|
+
runId: string;
|
|
151
|
+
snapshotId: string;
|
|
152
|
+
objectiveId: string;
|
|
153
|
+
requestId: string;
|
|
154
|
+
patternKey: string;
|
|
155
|
+
origin?: "autonomy";
|
|
156
|
+
};
|
|
157
|
+
autonomy_objective_blocked: {
|
|
158
|
+
runId: string;
|
|
159
|
+
snapshotId: string;
|
|
160
|
+
objectiveId: string;
|
|
161
|
+
reason: string;
|
|
162
|
+
questionId?: string;
|
|
163
|
+
patternKey?: string;
|
|
164
|
+
origin?: "autonomy";
|
|
165
|
+
};
|
|
166
|
+
autonomy_feedback_recorded: {
|
|
167
|
+
objectiveId: string;
|
|
168
|
+
patternKey: string;
|
|
169
|
+
outcome: string;
|
|
170
|
+
success: boolean;
|
|
171
|
+
};
|
|
172
|
+
question_asked: {
|
|
173
|
+
questionId: string;
|
|
174
|
+
objectiveId: string;
|
|
175
|
+
question: string;
|
|
176
|
+
questionType: string;
|
|
177
|
+
};
|
|
178
|
+
question_answered: {
|
|
179
|
+
questionId: string;
|
|
180
|
+
objectiveId: string;
|
|
181
|
+
status: "valid" | "invalid";
|
|
182
|
+
answerSummary?: string;
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Discriminated union of all event payload types
|
|
188
|
+
*/
|
|
189
|
+
export type EventPayload = EventTypePayloadMap[keyof EventTypePayloadMap];
|
|
190
|
+
|
|
191
|
+
export type EventType = keyof EventTypePayloadMap;
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* EventEnvelope parameterized by event type for type-safe access to payload
|
|
195
|
+
*
|
|
196
|
+
* Routing / meta fields (all optional):
|
|
197
|
+
* - from: originator identifier (e.g. "client", "agent:local1")
|
|
198
|
+
* - to: destination identifier (e.g. "broadcast", "worker:queue:default")
|
|
199
|
+
* - correlationId: threads a whole conversation turn together
|
|
200
|
+
* - parentId: links tool calls/results under their parent task event
|
|
201
|
+
* - turnId: one per user message; groups all downstream events
|
|
202
|
+
*/
|
|
203
|
+
export interface EventEnvelope<T extends EventType = EventType> {
|
|
204
|
+
protocolVersion: typeof PROTOCOL_VERSION;
|
|
205
|
+
id: string;
|
|
206
|
+
ts: string; // ISO-8601
|
|
207
|
+
sessionId: string;
|
|
208
|
+
type: T;
|
|
209
|
+
traceId?: string;
|
|
210
|
+
|
|
211
|
+
// Routing / meta
|
|
212
|
+
from?: string;
|
|
213
|
+
to?: string;
|
|
214
|
+
correlationId?: string;
|
|
215
|
+
parentId?: string;
|
|
216
|
+
turnId?: string;
|
|
217
|
+
|
|
218
|
+
payload: EventTypePayloadMap[T];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Any EventEnvelope - for cases where type is not known at compile time
|
|
223
|
+
*/
|
|
224
|
+
export type AnyEventEnvelope = EventEnvelope<EventType>;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* HTTP Request/Response types
|
|
228
|
+
*/
|
|
229
|
+
export interface CreateSessionResponse {
|
|
230
|
+
sessionId: string;
|
|
231
|
+
protocolVersion: typeof PROTOCOL_VERSION;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export interface MessageRequest {
|
|
235
|
+
text: string;
|
|
236
|
+
intent?: Record<string, unknown>;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export interface MessageResponse {
|
|
240
|
+
ok: boolean;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export interface ApprovalDecisionRequest {
|
|
244
|
+
decision: "approve" | "deny";
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export interface ApprovalDecisionResponse {
|
|
248
|
+
ok: boolean;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Command request (agent-friendly ingest)
|
|
253
|
+
*/
|
|
254
|
+
export interface CommandRequest {
|
|
255
|
+
type: EventType;
|
|
256
|
+
payload: Record<string, unknown>;
|
|
257
|
+
from?: string;
|
|
258
|
+
to?: string;
|
|
259
|
+
correlationId?: string;
|
|
260
|
+
turnId?: string;
|
|
261
|
+
parentId?: string;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface CommandResponse {
|
|
265
|
+
ok: boolean;
|
|
266
|
+
eventId?: string;
|
|
267
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import Ajv from "ajv";
|
|
2
|
+
import addFormats from "ajv-formats";
|
|
3
|
+
import envelopeSchema from "./schemas/envelope.schema.json";
|
|
4
|
+
import eventsSchema from "./schemas/events.schema.json";
|
|
5
|
+
|
|
6
|
+
const ajv = new Ajv({ strict: true });
|
|
7
|
+
addFormats(ajv);
|
|
8
|
+
|
|
9
|
+
// Register schemas to help $ref linking when present
|
|
10
|
+
try {
|
|
11
|
+
ajv.addSchema(envelopeSchema as object, "envelope.schema.json");
|
|
12
|
+
ajv.addSchema(eventsSchema as object, "events.schema.json");
|
|
13
|
+
} catch (_e) {
|
|
14
|
+
// ignore addSchema failures; we'll still compile below
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const validateEnvelopeBase = ajv.compile(envelopeSchema as unknown as object);
|
|
18
|
+
const validateEventPayload = ajv.compile(eventsSchema as unknown as object);
|
|
19
|
+
|
|
20
|
+
const validateMessageRequestSchema = ajv.compile({
|
|
21
|
+
type: "object",
|
|
22
|
+
required: ["text"],
|
|
23
|
+
properties: { text: { type: "string" }, intent: { type: "object", additionalProperties: true } },
|
|
24
|
+
additionalProperties: false,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const validateMessageResponseSchema = ajv.compile({
|
|
28
|
+
type: "object",
|
|
29
|
+
required: ["ok"],
|
|
30
|
+
properties: { ok: { type: "boolean" } },
|
|
31
|
+
additionalProperties: false,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const validateApprovalDecisionRequestSchema = ajv.compile({
|
|
35
|
+
type: "object",
|
|
36
|
+
required: ["decision"],
|
|
37
|
+
properties: { decision: { type: "string", enum: ["approve", "deny"] } },
|
|
38
|
+
additionalProperties: false,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const validateApprovalDecisionResponseSchema = ajv.compile({
|
|
42
|
+
type: "object",
|
|
43
|
+
required: ["ok"],
|
|
44
|
+
properties: { ok: { type: "boolean" } },
|
|
45
|
+
additionalProperties: false,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export interface ValidationResult {
|
|
49
|
+
ok: boolean;
|
|
50
|
+
errors?: string[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function validateEventEnvelope(data: unknown): ValidationResult {
|
|
54
|
+
const baseValid = validateEnvelopeBase(data);
|
|
55
|
+
if (!baseValid) {
|
|
56
|
+
const errors = (validateEnvelopeBase.errors ?? []).map((e) =>
|
|
57
|
+
`${e.instancePath || "/"} ${e.message ?? ""}`.trim(),
|
|
58
|
+
);
|
|
59
|
+
return { ok: false, errors };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Validate only the `{ type, payload }` pairing against the events schema.
|
|
63
|
+
const maybe = data as any;
|
|
64
|
+
const pair = { type: maybe?.type, payload: maybe?.payload };
|
|
65
|
+
|
|
66
|
+
const payloadValid = validateEventPayload(pair);
|
|
67
|
+
if (!payloadValid) {
|
|
68
|
+
const errors = (validateEventPayload.errors ?? []).map((e) =>
|
|
69
|
+
`${e.instancePath || "/"} ${e.message ?? ""}`.trim(),
|
|
70
|
+
);
|
|
71
|
+
return { ok: false, errors };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return { ok: true };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function validateMessageRequest(data: unknown): ValidationResult {
|
|
78
|
+
const valid = validateMessageRequestSchema(data);
|
|
79
|
+
const errors = valid
|
|
80
|
+
? undefined
|
|
81
|
+
: (validateMessageRequestSchema.errors ?? []).map((e) =>
|
|
82
|
+
`${e.instancePath || "/"} ${e.message ?? ""}`.trim(),
|
|
83
|
+
);
|
|
84
|
+
return { ok: valid, errors };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function validateMessageResponse(data: unknown): ValidationResult {
|
|
88
|
+
const valid = validateMessageResponseSchema(data);
|
|
89
|
+
const errors = valid
|
|
90
|
+
? undefined
|
|
91
|
+
: (validateMessageResponseSchema.errors ?? []).map((e) =>
|
|
92
|
+
`${e.instancePath || "/"} ${e.message ?? ""}`.trim(),
|
|
93
|
+
);
|
|
94
|
+
return { ok: valid, errors };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function validateCommandRequest(data: unknown): ValidationResult {
|
|
98
|
+
const allEventTypes = [
|
|
99
|
+
"log",
|
|
100
|
+
"scan_result",
|
|
101
|
+
"suggestions",
|
|
102
|
+
"diff_ready",
|
|
103
|
+
"approval_required",
|
|
104
|
+
"approved",
|
|
105
|
+
"denied",
|
|
106
|
+
"committed",
|
|
107
|
+
"assistant_message",
|
|
108
|
+
"error",
|
|
109
|
+
"done",
|
|
110
|
+
"agent_status",
|
|
111
|
+
"task_created",
|
|
112
|
+
"task_started",
|
|
113
|
+
"task_progress",
|
|
114
|
+
"task_completed",
|
|
115
|
+
"task_failed",
|
|
116
|
+
"tool_call",
|
|
117
|
+
"tool_result",
|
|
118
|
+
"delegate_request",
|
|
119
|
+
"delegate_response",
|
|
120
|
+
"job_enqueued",
|
|
121
|
+
"job_claimed",
|
|
122
|
+
"job_completed",
|
|
123
|
+
"job_failed",
|
|
124
|
+
"message",
|
|
125
|
+
"job_log",
|
|
126
|
+
"status",
|
|
127
|
+
];
|
|
128
|
+
const d = data as any;
|
|
129
|
+
if (!d || typeof d !== "object") return { ok: false, errors: ["Expected object"] };
|
|
130
|
+
if (!d.type || !allEventTypes.includes(d.type)) return { ok: false, errors: ["Invalid type"] };
|
|
131
|
+
if (!d.payload || typeof d.payload !== "object")
|
|
132
|
+
return { ok: false, errors: ["Invalid payload"] };
|
|
133
|
+
return { ok: true };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function validateApprovalDecisionRequest(data: unknown): ValidationResult {
|
|
137
|
+
const valid = validateApprovalDecisionRequestSchema(data);
|
|
138
|
+
const errors = valid
|
|
139
|
+
? undefined
|
|
140
|
+
: (validateApprovalDecisionRequestSchema.errors ?? []).map((e) =>
|
|
141
|
+
`${e.instancePath || "/"} ${e.message ?? ""}`.trim(),
|
|
142
|
+
);
|
|
143
|
+
return { ok: valid, errors };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function validateApprovalDecisionResponse(data: unknown): ValidationResult {
|
|
147
|
+
const valid = validateApprovalDecisionResponseSchema(data);
|
|
148
|
+
const errors = valid
|
|
149
|
+
? undefined
|
|
150
|
+
: (validateApprovalDecisionResponseSchema.errors ?? []).map((e) =>
|
|
151
|
+
`${e.instancePath || "/"} ${e.message ?? ""}`.trim(),
|
|
152
|
+
);
|
|
153
|
+
return { ok: valid, errors };
|
|
154
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import Ajv from "ajv";
|
|
2
|
+
import addFormats from "ajv-formats";
|
|
3
|
+
import { readFileSync } from "fs";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { dirname, join } from "path";
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const ajv = new Ajv({ strict: true });
|
|
9
|
+
|
|
10
|
+
// Add format validators for date-time, etc.
|
|
11
|
+
addFormats(ajv);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Expected runtime layout (after `npm run build` / `bun --cwd packages/protocol build`):
|
|
15
|
+
*
|
|
16
|
+
* packages/protocol/dist/index.js
|
|
17
|
+
* packages/protocol/dist/validate.js
|
|
18
|
+
* packages/protocol/dist/schemas/*.json
|
|
19
|
+
*
|
|
20
|
+
* The loader below prefers `dist/schemas` when running compiled JS and falls
|
|
21
|
+
* back to `../src/schemas` during development. The `scripts/copy-schemas.js`
|
|
22
|
+
* build step copies `src/schemas` into `dist/schemas` to satisfy runtime loads.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Load schema from file. Deterministic order:
|
|
27
|
+
* 1. Built/runtime: `dist/schemas` (when running compiled JS)
|
|
28
|
+
* 2. Development: `src/schemas` (when running from source)
|
|
29
|
+
*/
|
|
30
|
+
function loadSchema(filename: string): Record<string, unknown> {
|
|
31
|
+
const distSchemasPath = join(__dirname, "schemas", filename); // dist/schemas when compiled
|
|
32
|
+
const srcSchemasPath = join(__dirname, "..", "src", "schemas", filename); // src/schemas during development
|
|
33
|
+
const envSchemasDir = String(process.env.PUSHPALS_PROTOCOL_SCHEMAS_DIR ?? "").trim();
|
|
34
|
+
const envSchemasPath = envSchemasDir ? join(envSchemasDir, filename) : "";
|
|
35
|
+
const candidates = [distSchemasPath, srcSchemasPath, envSchemasPath].filter(Boolean);
|
|
36
|
+
|
|
37
|
+
for (const pathValue of candidates) {
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(readFileSync(pathValue, "utf-8"));
|
|
40
|
+
} catch {
|
|
41
|
+
// try next path
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
throw new Error(
|
|
46
|
+
`Failed to load schema ${filename}. Expected at dist/schemas (build), src/schemas (dev), or PUSHPALS_PROTOCOL_SCHEMAS_DIR.`,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Load schemas
|
|
51
|
+
const envelopeSchema = loadSchema("envelope.schema.json");
|
|
52
|
+
const eventsSchema = loadSchema("events.schema.json");
|
|
53
|
+
|
|
54
|
+
// Register schemas with AJV (helps with $ref resolution across files)
|
|
55
|
+
try {
|
|
56
|
+
ajv.addSchema(envelopeSchema as object, "envelope.schema.json");
|
|
57
|
+
ajv.addSchema(eventsSchema as object, "events.schema.json");
|
|
58
|
+
} catch (_e) {
|
|
59
|
+
// addSchema may throw in strict modes for malformed schemas; compilation below
|
|
60
|
+
// will still attempt to compile standalone validators.
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Compile validators
|
|
64
|
+
const validateEnvelopeBase = ajv.compile(envelopeSchema as object);
|
|
65
|
+
const validateEventPayload = ajv.compile(eventsSchema as object);
|
|
66
|
+
const validateMessageRequestSchema = ajv.compile({
|
|
67
|
+
type: "object",
|
|
68
|
+
required: ["text"],
|
|
69
|
+
properties: {
|
|
70
|
+
text: { type: "string" },
|
|
71
|
+
intent: { type: "object", additionalProperties: true },
|
|
72
|
+
},
|
|
73
|
+
additionalProperties: false,
|
|
74
|
+
});
|
|
75
|
+
const validateMessageResponseSchema = ajv.compile({
|
|
76
|
+
type: "object",
|
|
77
|
+
required: ["ok"],
|
|
78
|
+
properties: {
|
|
79
|
+
ok: { type: "boolean" },
|
|
80
|
+
},
|
|
81
|
+
additionalProperties: false,
|
|
82
|
+
});
|
|
83
|
+
const validateApprovalDecisionRequestSchema = ajv.compile({
|
|
84
|
+
type: "object",
|
|
85
|
+
required: ["decision"],
|
|
86
|
+
properties: {
|
|
87
|
+
decision: {
|
|
88
|
+
type: "string",
|
|
89
|
+
enum: ["approve", "deny"],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
additionalProperties: false,
|
|
93
|
+
});
|
|
94
|
+
const validateApprovalDecisionResponseSchema = ajv.compile({
|
|
95
|
+
type: "object",
|
|
96
|
+
required: ["ok"],
|
|
97
|
+
properties: {
|
|
98
|
+
ok: { type: "boolean" },
|
|
99
|
+
},
|
|
100
|
+
additionalProperties: false,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
export interface ValidationResult {
|
|
104
|
+
ok: boolean;
|
|
105
|
+
errors?: string[];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Validate an EventEnvelope against the full schema
|
|
110
|
+
*/
|
|
111
|
+
export function validateEventEnvelope(data: unknown): ValidationResult {
|
|
112
|
+
const baseValid = validateEnvelopeBase(data);
|
|
113
|
+
if (!baseValid) {
|
|
114
|
+
const errors = (validateEnvelopeBase.errors ?? []).map((e) =>
|
|
115
|
+
`${e.instancePath || "/"} ${e.message ?? ""}`.trim(),
|
|
116
|
+
);
|
|
117
|
+
return { ok: false, errors };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Validate only the `{ type, payload }` pairing against the events schema.
|
|
121
|
+
// This ensures branch validation focuses on the event discriminant and
|
|
122
|
+
// its payload shape, while envelope-level fields are handled above.
|
|
123
|
+
const maybe = data as any;
|
|
124
|
+
const pair = { type: maybe?.type, payload: maybe?.payload };
|
|
125
|
+
|
|
126
|
+
const payloadValid = validateEventPayload(pair);
|
|
127
|
+
if (!payloadValid) {
|
|
128
|
+
const errors = (validateEventPayload.errors ?? []).map((e) =>
|
|
129
|
+
`${e.instancePath || "/"} ${e.message ?? ""}`.trim(),
|
|
130
|
+
);
|
|
131
|
+
return { ok: false, errors };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return { ok: true };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function validateMessageRequest(data: unknown): ValidationResult {
|
|
138
|
+
const valid = validateMessageRequestSchema(data);
|
|
139
|
+
return {
|
|
140
|
+
ok: valid,
|
|
141
|
+
errors: valid ? undefined : ajv.errorsText(validateMessageRequestSchema.errors).split(", "),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function validateMessageResponse(data: unknown): ValidationResult {
|
|
146
|
+
const valid = validateMessageResponseSchema(data);
|
|
147
|
+
return {
|
|
148
|
+
ok: valid,
|
|
149
|
+
errors: valid ? undefined : ajv.errorsText(validateMessageResponseSchema.errors).split(", "),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function validateApprovalDecisionRequest(data: unknown): ValidationResult {
|
|
154
|
+
const valid = validateApprovalDecisionRequestSchema(data);
|
|
155
|
+
return {
|
|
156
|
+
ok: valid,
|
|
157
|
+
errors: valid
|
|
158
|
+
? undefined
|
|
159
|
+
: ajv.errorsText(validateApprovalDecisionRequestSchema.errors).split(", "),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function validateApprovalDecisionResponse(data: unknown): ValidationResult {
|
|
164
|
+
const valid = validateApprovalDecisionResponseSchema(data);
|
|
165
|
+
return {
|
|
166
|
+
ok: valid,
|
|
167
|
+
errors: valid
|
|
168
|
+
? undefined
|
|
169
|
+
: ajv.errorsText(validateApprovalDecisionResponseSchema.errors).split(", "),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Command request schema (agent-friendly ingest)
|
|
174
|
+
const allEventTypes = [
|
|
175
|
+
"log",
|
|
176
|
+
"scan_result",
|
|
177
|
+
"suggestions",
|
|
178
|
+
"diff_ready",
|
|
179
|
+
"approval_required",
|
|
180
|
+
"approved",
|
|
181
|
+
"denied",
|
|
182
|
+
"committed",
|
|
183
|
+
"assistant_message",
|
|
184
|
+
"error",
|
|
185
|
+
"done",
|
|
186
|
+
"agent_status",
|
|
187
|
+
"task_created",
|
|
188
|
+
"task_started",
|
|
189
|
+
"task_progress",
|
|
190
|
+
"task_completed",
|
|
191
|
+
"task_failed",
|
|
192
|
+
"tool_call",
|
|
193
|
+
"tool_result",
|
|
194
|
+
"delegate_request",
|
|
195
|
+
"delegate_response",
|
|
196
|
+
"job_enqueued",
|
|
197
|
+
"job_claimed",
|
|
198
|
+
"job_completed",
|
|
199
|
+
"job_failed",
|
|
200
|
+
"message",
|
|
201
|
+
"job_log",
|
|
202
|
+
"status",
|
|
203
|
+
"autonomy_cycle_started",
|
|
204
|
+
"autonomy_candidates_generated",
|
|
205
|
+
"autonomy_objective_dispatched",
|
|
206
|
+
"autonomy_objective_blocked",
|
|
207
|
+
"autonomy_feedback_recorded",
|
|
208
|
+
"question_asked",
|
|
209
|
+
"question_answered",
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
const validateCommandRequestSchema = ajv.compile({
|
|
213
|
+
type: "object",
|
|
214
|
+
required: ["type", "payload"],
|
|
215
|
+
properties: {
|
|
216
|
+
type: { type: "string", enum: allEventTypes },
|
|
217
|
+
payload: { type: "object", additionalProperties: true },
|
|
218
|
+
from: { type: "string" },
|
|
219
|
+
to: { type: "string" },
|
|
220
|
+
correlationId: { type: "string" },
|
|
221
|
+
turnId: { type: "string" },
|
|
222
|
+
parentId: { type: "string" },
|
|
223
|
+
},
|
|
224
|
+
additionalProperties: false,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
export function validateCommandRequest(data: unknown): ValidationResult {
|
|
228
|
+
const valid = validateCommandRequestSchema(data);
|
|
229
|
+
return {
|
|
230
|
+
ok: valid,
|
|
231
|
+
errors: valid ? undefined : ajv.errorsText(validateCommandRequestSchema.errors).split(", "),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PROTOCOL_VERSION = "0.1.0";
|