openpond 0.0.30-bootstrap.0

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.
Files changed (193) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/LICENSE +21 -0
  3. package/README.md +162 -0
  4. package/RELEASE.md +20 -0
  5. package/dist/api/core.d.ts +22 -0
  6. package/dist/api/deployments.d.ts +30 -0
  7. package/dist/api/types.d.ts +624 -0
  8. package/dist/api.d.ts +90 -0
  9. package/dist/cache.d.ts +22 -0
  10. package/dist/chunks/app-layer-YKXEQGOW.js +163 -0
  11. package/dist/chunks/apps-GGSIN2ZF.js +539 -0
  12. package/dist/chunks/chunk-3CC6MH35.js +1663 -0
  13. package/dist/chunks/chunk-3Y4EL3P6.js +1090 -0
  14. package/dist/chunks/chunk-4HYJG2JE.js +3875 -0
  15. package/dist/chunks/chunk-5G4E547X.js +294 -0
  16. package/dist/chunks/chunk-74O6FGVN.js +598 -0
  17. package/dist/chunks/chunk-AUJLPKGI.js +632 -0
  18. package/dist/chunks/chunk-BYAPLT37.js +744 -0
  19. package/dist/chunks/chunk-FUP7EGT7.js +1845 -0
  20. package/dist/chunks/chunk-I6HN3ZGW.js +88 -0
  21. package/dist/chunks/chunk-KRKGHVMQ.js +294 -0
  22. package/dist/chunks/chunk-LHYK6QO2.js +1300 -0
  23. package/dist/chunks/chunk-NLBKZZEK.js +224 -0
  24. package/dist/chunks/chunk-OG75GMVO.js +1 -0
  25. package/dist/chunks/chunk-Q57SVPEH.js +11663 -0
  26. package/dist/chunks/chunk-UACZUFIA.js +47 -0
  27. package/dist/chunks/chunk-VQX33YAG.js +5135 -0
  28. package/dist/chunks/chunk-W5GKHH77.js +168 -0
  29. package/dist/chunks/chunk-WYDVQOBL.js +17581 -0
  30. package/dist/chunks/chunk-XSAXENQD.js +4167 -0
  31. package/dist/chunks/chunk-Y6G6WRTG.js +210 -0
  32. package/dist/chunks/cli-NVKNE4AZ.js +11 -0
  33. package/dist/chunks/cli-X3HPLSOV.js +19 -0
  34. package/dist/chunks/core-commands-IM2UKLU4.js +41 -0
  35. package/dist/chunks/extend-YXCBULPO.js +84 -0
  36. package/dist/chunks/harness-GMK6IY2U.js +140 -0
  37. package/dist/chunks/help-LMH5FGT2.js +65 -0
  38. package/dist/chunks/opchat-BWXZ32GX.js +169 -0
  39. package/dist/chunks/organizations-4ISQRGMQ.js +738 -0
  40. package/dist/chunks/profile-NLDZDT3L.js +1110 -0
  41. package/dist/chunks/project-agent-ZPVO25HA.js +892 -0
  42. package/dist/chunks/sandbox-command-5GFJ2U3V.js +1652 -0
  43. package/dist/chunks/sandbox-template-LZN4F55Z.js +1646 -0
  44. package/dist/chunks/src-3RBI4DNN.js +3595 -0
  45. package/dist/chunks/src-EYXQS6XD.js +73427 -0
  46. package/dist/chunks/teams-bot-WP3FHKGA.js +137 -0
  47. package/dist/cli.js +77 -0
  48. package/dist/client-types.d.ts +222 -0
  49. package/dist/client.d.ts +19 -0
  50. package/dist/config.d.ts +184 -0
  51. package/dist/hosted-chat.d.ts +162 -0
  52. package/dist/index.d.ts +5 -0
  53. package/dist/index.js +25481 -0
  54. package/dist/indicators.d.ts +38 -0
  55. package/dist/opchat-smoke.d.ts +47 -0
  56. package/dist/private-json-file.d.ts +3 -0
  57. package/dist/profile/local-profile-types.d.ts +160 -0
  58. package/dist/profile/local-profile.d.ts +69 -0
  59. package/dist/profile/profile-catalog.d.ts +14 -0
  60. package/dist/profile/profile-git.d.ts +17 -0
  61. package/dist/profile/profile-promotion.d.ts +21 -0
  62. package/dist/profile/profile-setup-gate.d.ts +22 -0
  63. package/dist/profile/profile-skills.d.ts +30 -0
  64. package/dist/profile/profile-source-upload.d.ts +29 -0
  65. package/dist/profile/source-upload-cache.d.ts +29 -0
  66. package/dist/sandbox/async-request-options.d.ts +2 -0
  67. package/dist/sandbox/client-handles.d.ts +113 -0
  68. package/dist/sandbox/client.d.ts +348 -0
  69. package/dist/sandbox/runtime-handle.d.ts +36 -0
  70. package/dist/sandbox/sandbox-instance-client.d.ts +72 -0
  71. package/dist/sandbox/smoke.d.ts +3 -0
  72. package/dist/sandbox/stream.d.ts +7 -0
  73. package/dist/sandbox/types/billing.d.ts +176 -0
  74. package/dist/sandbox/types/client.d.ts +103 -0
  75. package/dist/sandbox/types/core.d.ts +140 -0
  76. package/dist/sandbox/types/index.d.ts +14 -0
  77. package/dist/sandbox/types/io.d.ts +240 -0
  78. package/dist/sandbox/types/microsoft-teams-bot.d.ts +51 -0
  79. package/dist/sandbox/types/org-project-agent.d.ts +610 -0
  80. package/dist/sandbox/types/record.d.ts +53 -0
  81. package/dist/sandbox/types/responses.d.ts +295 -0
  82. package/dist/sandbox/types/runtime-profiles.d.ts +15 -0
  83. package/dist/sandbox/types/runtime.d.ts +152 -0
  84. package/dist/sandbox/types/schedules.d.ts +141 -0
  85. package/dist/sandbox/types/snapshots.d.ts +205 -0
  86. package/dist/sandbox/types/workflow-modes.d.ts +1 -0
  87. package/dist/sandbox/types/workload-sources.d.ts +20 -0
  88. package/dist/sandbox/url.d.ts +2 -0
  89. package/dist/sandbox-template/manifest.d.ts +304 -0
  90. package/dist/sandbox-template/manifest.js +18381 -0
  91. package/dist/skills/openpond-cli/SKILL.md +49 -0
  92. package/dist/skills/openpond-desktop-harness/SKILL.md +113 -0
  93. package/dist/skills/openpond-desktop-harness/agents/openai.yaml +4 -0
  94. package/dist/skills/openpond-taskset-authoring/SKILL.md +38 -0
  95. package/dist/skills/openpond-taskset-authoring/references/graders-and-rewards.md +11 -0
  96. package/dist/skills/openpond-taskset-authoring/references/method-selection.md +12 -0
  97. package/dist/skills/openpond-taskset-authoring/references/privacy-and-provenance.md +8 -0
  98. package/dist/skills/openpond-taskset-authoring/references/task-design.md +13 -0
  99. package/dist/stream.d.ts +20 -0
  100. package/dist/types.d.ts +57 -0
  101. package/dist/urls.d.ts +4 -0
  102. package/dist/web/assets/AppsView-Bu-Nc3Ew.js +1 -0
  103. package/dist/web/assets/AppsView-DIhprTIC.css +1 -0
  104. package/dist/web/assets/BrowserSidebar-CCguQ-DR.js +1 -0
  105. package/dist/web/assets/BrowserSidebar-D9i7gQzw.css +1 -0
  106. package/dist/web/assets/CloudWorkView-DNoPIlMj.css +1 -0
  107. package/dist/web/assets/CloudWorkView-Dr3xqLE5.js +1 -0
  108. package/dist/web/assets/CommandMenu-CnodJFHD.js +1 -0
  109. package/dist/web/assets/CommandMenu-hNQCm8mw.css +1 -0
  110. package/dist/web/assets/CommunityView-CIbjNUZr.js +1 -0
  111. package/dist/web/assets/CommunityView-DSik4sd2.css +1 -0
  112. package/dist/web/assets/GetStartedView-C3ZrnEz5.css +1 -0
  113. package/dist/web/assets/GetStartedView-C8nyH_PT.js +1 -0
  114. package/dist/web/assets/LabsExtensions-GtJzxDnK.js +1 -0
  115. package/dist/web/assets/LabsProfile-DG7rWYur.js +1 -0
  116. package/dist/web/assets/LabsRoute-D6MDHBzq.js +2 -0
  117. package/dist/web/assets/LabsRoute-TEt9j1vB.css +1 -0
  118. package/dist/web/assets/LabsSignals-BfsDN86w.js +1 -0
  119. package/dist/web/assets/LabsSignals-D6iA6Ryl.css +1 -0
  120. package/dist/web/assets/NewProjectDialog-CaKiCoVS.js +1 -0
  121. package/dist/web/assets/ProfileSettingsSection-DTQS2DVL.css +1 -0
  122. package/dist/web/assets/ProfileSettingsSection-DxEzoF3K.js +1 -0
  123. package/dist/web/assets/ProfileView-CEscW7Ru.js +1 -0
  124. package/dist/web/assets/SettingsView-DwbGu8Kj.css +1 -0
  125. package/dist/web/assets/SettingsView-JIRR_FO4.js +63 -0
  126. package/dist/web/assets/TeamChatView-B5M1ThhJ.js +3 -0
  127. package/dist/web/assets/TeamChatView-Bx44q3WV.css +1 -0
  128. package/dist/web/assets/TerminalOverlay-C1iP4OS1.js +19 -0
  129. package/dist/web/assets/TerminalOverlay-DwBiZLwk.css +1 -0
  130. package/dist/web/assets/TrainingSettingsSection-BDYvI2te.js +1 -0
  131. package/dist/web/assets/TrainingView-CoCFK6W3.js +5 -0
  132. package/dist/web/assets/WorkspaceDiffPanel-CMxKQeo7.js +14 -0
  133. package/dist/web/assets/WorkspaceEnvironmentMenu-B8JJbwIz.js +32 -0
  134. package/dist/web/assets/WorkspaceEnvironmentMenu-DGIFdG3o.css +1 -0
  135. package/dist/web/assets/WorkspaceGitDialogs-B4sp9BGn.js +1 -0
  136. package/dist/web/assets/WorkspaceMonacoEditor-BBqimG5X.js +845 -0
  137. package/dist/web/assets/WorkspaceMonacoEditor-BwKcXIZw.css +1 -0
  138. package/dist/web/assets/boxes-DAaDUabq.js +1 -0
  139. package/dist/web/assets/codicon-ngg6Pgfi.ttf +0 -0
  140. package/dist/web/assets/css.worker-DBVD8oXr.js +93 -0
  141. package/dist/web/assets/cssMode-CBF1mieJ.js +1 -0
  142. package/dist/web/assets/dockerfile-BG73LgW2.js +1 -0
  143. package/dist/web/assets/editor.worker-CdQrwHl8.js +26 -0
  144. package/dist/web/assets/git-commit-horizontal-CAickXpN.js +1 -0
  145. package/dist/web/assets/git-dialogs-BZDqS1eT.css +1 -0
  146. package/dist/web/assets/html.worker-CwpTb9lJ.js +470 -0
  147. package/dist/web/assets/htmlMode-2u8OHtWO.js +1 -0
  148. package/dist/web/assets/index-BdY6A-YN.css +1 -0
  149. package/dist/web/assets/index-CU2qhUYV.js +195 -0
  150. package/dist/web/assets/index-Dz2B3-4C.js +1 -0
  151. package/dist/web/assets/info-CQNTh78o.js +1 -0
  152. package/dist/web/assets/json.worker-BoL8UZqY.js +58 -0
  153. package/dist/web/assets/jsonMode-C4CWXViX.js +7 -0
  154. package/dist/web/assets/lspLanguageFeatures-CBs_BOlB.js +4 -0
  155. package/dist/web/assets/markdown-BFxVWTOG.js +1 -0
  156. package/dist/web/assets/monaco.contribution-C1LUxAKf.js +2 -0
  157. package/dist/web/assets/monaco.contribution-Cpf6dWsh.js +2 -0
  158. package/dist/web/assets/monaco.contribution-lOXpG2xQ.js +2 -0
  159. package/dist/web/assets/monaco.contribution-sd3cFkcN.js +2 -0
  160. package/dist/web/assets/monitor-DsIswrMV.js +1 -0
  161. package/dist/web/assets/play-CeRw21NV.js +1 -0
  162. package/dist/web/assets/python-CXYq3BUl.js +1 -0
  163. package/dist/web/assets/refresh-cw-N5b_6AP5.js +1 -0
  164. package/dist/web/assets/reply-BSKjZrJg.js +1 -0
  165. package/dist/web/assets/save-D0oHH1a3.js +1 -0
  166. package/dist/web/assets/settings-lists-BEg6LMcy.css +1 -0
  167. package/dist/web/assets/shell-DiJ1NA_G.js +1 -0
  168. package/dist/web/assets/toggleHighContrast-B7NOMXDY.css +1 -0
  169. package/dist/web/assets/toggleHighContrast-DRjlcwes.js +62 -0
  170. package/dist/web/assets/training-model-data-BPrc68VG.js +1 -0
  171. package/dist/web/assets/ts.worker-BH9nVgjN.js +67718 -0
  172. package/dist/web/assets/tsMode-x-1A7ZcB.js +11 -0
  173. package/dist/web/assets/wifi-off-Dt52ofzh.js +1 -0
  174. package/dist/web/assets/workers-Ciff6Dzi.js +1 -0
  175. package/dist/web/assets/yaml-CB67Kxnz.js +1 -0
  176. package/dist/web/connected-apps/github.svg +3 -0
  177. package/dist/web/connected-apps/google.svg +6 -0
  178. package/dist/web/connected-apps/linear.svg +3 -0
  179. package/dist/web/connected-apps/microsoft.svg +6 -0
  180. package/dist/web/connected-apps/notion.svg +3 -0
  181. package/dist/web/connected-apps/openpond-mcp.svg +4 -0
  182. package/dist/web/connected-apps/slack.svg +6 -0
  183. package/dist/web/connected-apps/x.svg +3 -0
  184. package/dist/web/favicon-16x16.png +0 -0
  185. package/dist/web/favicon-32x32.png +0 -0
  186. package/dist/web/index.html +16 -0
  187. package/dist/web/openpond-icon.png +0 -0
  188. package/dist/web/openpond-wordlogo-white.png +0 -0
  189. package/dist/web/tokens/eth.svg +1 -0
  190. package/dist/web/tokens/usdc.svg +1 -0
  191. package/docs/agent-edit-check-status-json.md +132 -0
  192. package/docs/command-reference.md +695 -0
  193. package/package.json +84 -0
@@ -0,0 +1,3875 @@
1
+ import { createRequire as __openpondCreateRequire } from "node:module"; var require = __openpondCreateRequire(import.meta.url);
2
+ import {
3
+ resolveOpChatApiBaseUrl,
4
+ sendHostedChatTurn
5
+ } from "./chunk-NLBKZZEK.js";
6
+ import {
7
+ CreatePipelinePlanSchema,
8
+ CreatePipelineSnapshotSchema,
9
+ WorkflowCaptureArtifactSchema,
10
+ createPipelineActionShapeFromMetadata,
11
+ inferCreatePipelineActionShape
12
+ } from "./chunk-VQX33YAG.js";
13
+ import {
14
+ external_exports
15
+ } from "./chunk-WYDVQOBL.js";
16
+ import {
17
+ apiFetch,
18
+ loadGlobalConfig,
19
+ readApiJson,
20
+ runShellCommand
21
+ } from "./chunk-XSAXENQD.js";
22
+
23
+ // src/goal/cli.ts
24
+ import { randomUUID as randomUUID9 } from "node:crypto";
25
+ import { readFile as readFile3 } from "node:fs/promises";
26
+
27
+ // src/goal/config.ts
28
+ import { randomUUID } from "node:crypto";
29
+ import os from "node:os";
30
+ import { join, resolve } from "node:path";
31
+ var DEFAULT_GOAL_EXECUTION_POLICY = {
32
+ sourceUpdates: "auto_commit_push_default_branch",
33
+ externalEffects: "approval_required",
34
+ budgetEscalation: "approval_required"
35
+ };
36
+ var DEFAULT_GOAL_VERIFICATION = {
37
+ commands: [],
38
+ requiredChecks: [],
39
+ successCriteria: [],
40
+ regressionPolicy: "existing_checks_must_pass",
41
+ artifactRefs: []
42
+ };
43
+ var DEFAULT_GOAL_BUDGET = {
44
+ maxIterations: 8,
45
+ maxRuntimeMinutes: 60,
46
+ maxModelCostUsd: null
47
+ };
48
+ function goalKindDefaultProfile(kind) {
49
+ return kind === "create_agent" || kind === "update_agent" ? "openpond_agent" : "generic_coding";
50
+ }
51
+ function defaultPromptPackForGoal(kind) {
52
+ if (kind === "create_agent") return "openpond_agent_create_v1";
53
+ if (kind === "update_agent") return "openpond_agent_update_v1";
54
+ return "generic_coding_v1";
55
+ }
56
+ function createGoalState(input) {
57
+ const kind = input.kind ?? "general_code_goal";
58
+ const profile = input.profile ?? goalKindDefaultProfile(kind);
59
+ const now = (/* @__PURE__ */ new Date()).toISOString();
60
+ return {
61
+ id: `goal_${randomUUID()}`,
62
+ teamId: input.teamId ?? null,
63
+ projectId: input.projectId ?? null,
64
+ agentId: input.agentId ?? null,
65
+ workItemId: input.workItemId ?? null,
66
+ conversationId: input.conversationId ?? null,
67
+ sandboxId: null,
68
+ runtimeId: null,
69
+ kind,
70
+ profile,
71
+ promptPack: input.promptPack ?? defaultPromptPackForGoal(kind),
72
+ objective: input.objective,
73
+ status: "queued",
74
+ executionPolicy: DEFAULT_GOAL_EXECUTION_POLICY,
75
+ verification: {
76
+ ...DEFAULT_GOAL_VERIFICATION,
77
+ ...input.verification ?? {},
78
+ commands: input.verification?.commands ?? DEFAULT_GOAL_VERIFICATION.commands,
79
+ requiredChecks: input.verification?.requiredChecks ?? DEFAULT_GOAL_VERIFICATION.requiredChecks,
80
+ successCriteria: input.verification?.successCriteria ?? DEFAULT_GOAL_VERIFICATION.successCriteria,
81
+ artifactRefs: input.verification?.artifactRefs ?? DEFAULT_GOAL_VERIFICATION.artifactRefs
82
+ },
83
+ constraints: [],
84
+ evidenceRefs: [],
85
+ budget: DEFAULT_GOAL_BUDGET,
86
+ questions: [],
87
+ answers: [],
88
+ approvals: [],
89
+ events: [
90
+ {
91
+ id: `event_${randomUUID()}`,
92
+ goalId: "",
93
+ iterationId: null,
94
+ kind: "goal.created",
95
+ summary: "Goal created",
96
+ payload: { kind, profile },
97
+ createdAt: now
98
+ }
99
+ ],
100
+ createdAt: now,
101
+ updatedAt: now
102
+ };
103
+ }
104
+ function normalizeGoalState(state) {
105
+ return {
106
+ ...state,
107
+ events: state.events.map(
108
+ (event) => event.goalId ? event : { ...event, goalId: state.id }
109
+ )
110
+ };
111
+ }
112
+ function resolveGoalWorkspace(cwd) {
113
+ return resolve(cwd || process.cwd());
114
+ }
115
+ var DEFAULT_GOAL_STORAGE_LOCATION = "global";
116
+ function parseGoalStorageLocation(value) {
117
+ const normalized = value?.trim().toLowerCase();
118
+ if (!normalized) return null;
119
+ if (normalized === "global" || normalized === "home" || normalized === "user-home") return "global";
120
+ if (normalized === "workspace" || normalized === "cwd" || normalized === "working-directory") return "workspace";
121
+ throw new Error("goal storage must be global or workspace");
122
+ }
123
+ async function resolveGoalStorageLocation(explicit) {
124
+ const explicitLocation = parseGoalStorageLocation(explicit);
125
+ if (explicitLocation) return explicitLocation;
126
+ const envLocation = parseGoalStorageLocation(
127
+ process.env.OPENPOND_GOAL_STORAGE_LOCATION || process.env.OPENPOND_GOAL_STORAGE
128
+ );
129
+ if (envLocation) return envLocation;
130
+ const config = await loadGlobalConfig().catch(() => null);
131
+ return config?.goalStorageLocation ?? DEFAULT_GOAL_STORAGE_LOCATION;
132
+ }
133
+ function resolveGoalStorageRoot(input) {
134
+ return input.location === "workspace" ? resolveGoalWorkspace(input.cwd) : os.homedir();
135
+ }
136
+ function goalStateDisplayPath(input) {
137
+ const home = os.homedir();
138
+ const relative3 = join(".openpond", "goals", input.goalId, input.fileName);
139
+ return input.storageRoot === home ? `~/${relative3}` : relative3;
140
+ }
141
+
142
+ // src/goal/create-pipeline.ts
143
+ import { randomUUID as randomUUID2 } from "node:crypto";
144
+ function shouldCreatePipelineForGoal(kind) {
145
+ return kind === "create_agent" || kind === "update_agent";
146
+ }
147
+ function createInitialCreatePipeline(input) {
148
+ const now = (/* @__PURE__ */ new Date()).toISOString();
149
+ const requestId = `create_request_${randomUUID2()}`;
150
+ const planId = `create_plan_${randomUUID2()}`;
151
+ const approvalId = `approval_${randomUUID2()}`;
152
+ const operation = input.goal.kind === "update_agent" ? "edit" : "create";
153
+ const createAgentId = input.goal.agentId ?? slugFromObjective(input.goal.objective);
154
+ const sourceRoot = sourceRootPathForAgent(
155
+ operation === "edit" && input.goal.agentId ? input.goal.agentId : createAgentId
156
+ );
157
+ const request = {
158
+ schemaVersion: "openpond.createPipeline.request.v1",
159
+ id: requestId,
160
+ operation,
161
+ surface: input.surface,
162
+ command: input.command,
163
+ objective: input.goal.objective,
164
+ adapter: {
165
+ kind: "local",
166
+ sourceAuthority: "local_profile",
167
+ activeProfile: input.profile?.activeProfile ?? null,
168
+ repoPath: input.profile?.repoPath ?? null,
169
+ sourcePath: input.profile?.sourcePath ?? null,
170
+ localHead: input.profile?.localHead ?? null,
171
+ confirmationPolicy: "always_require_plan_approval"
172
+ },
173
+ actor: {
174
+ id: input.actorId ?? null,
175
+ kind: "user",
176
+ label: null
177
+ },
178
+ scope: {
179
+ conversationId: input.goal.conversationId,
180
+ workItemId: input.goal.workItemId,
181
+ projectId: input.goal.projectId,
182
+ targetProject: input.goal.projectId ? {
183
+ id: input.goal.projectId,
184
+ name: null,
185
+ workspacePath: null,
186
+ sourceRef: null,
187
+ baseSha: null
188
+ } : null
189
+ },
190
+ context: {
191
+ messageIds: [],
192
+ conversationExcerpts: [],
193
+ attachments: [],
194
+ apps: [],
195
+ tools: [],
196
+ targetRepoAssumptions: []
197
+ },
198
+ targetAgent: {
199
+ agentId: input.goal.agentId,
200
+ displayName: null,
201
+ defaultActionKey: input.goal.agentId ? `${input.goal.agentId}.chat` : "chat"
202
+ },
203
+ metadata: {
204
+ goalKind: input.goal.kind,
205
+ promptPack: input.goal.promptPack,
206
+ sourceOwnership: "Profile source is durable agent code/config; local Goal state is operational run history and is not committed by default.",
207
+ stateOwnership: "Local questions, answers, approvals, events, create plans, and workflow capture are stored under the configured local Goal storage root."
208
+ },
209
+ createdAt: now
210
+ };
211
+ const parsedRequest = request;
212
+ const actionShape = inferCreatePipelineActionShape(parsedRequest);
213
+ const actionShapeDecisionSource = createPipelineActionShapeFromMetadata(
214
+ parsedRequest.metadata
215
+ ) ? "request_metadata" : "default_chat_fallback";
216
+ const plan = CreatePipelinePlanSchema.parse({
217
+ schemaVersion: "openpond.createPipeline.plan.v1",
218
+ id: planId,
219
+ goalId: input.goal.id,
220
+ requestId,
221
+ status: "pending_approval",
222
+ objective: input.goal.objective,
223
+ summary: operation === "edit" ? "Edit the selected source-backed OpenPond profile agent after this plan is approved." : "Create a source-backed OpenPond profile agent after this plan is approved.",
224
+ capturedContextSummary: input.surface === "local_extend" ? "Local extend request from the active profile source. No prior chat context was attached." : "Direct prompt request. No prior chat context was attached.",
225
+ defaultChatAction: {
226
+ key: input.goal.agentId ? `${input.goal.agentId}.chat` : "chat",
227
+ label: "Default chat",
228
+ required: true
229
+ },
230
+ sourcePlan: [
231
+ {
232
+ path: sourceRoot,
233
+ operation: operation === "edit" ? "update" : "create",
234
+ reason: operation === "edit" ? "Revise the selected profile agent source while preserving the default chat route." : "Implement the new profile agent source without overwriting existing profile agents."
235
+ },
236
+ {
237
+ path: "settings/profile.yaml",
238
+ operation: "update",
239
+ reason: "Register the agent and expose the default chat action in the local profile catalog."
240
+ },
241
+ {
242
+ path: sdkOutputPath(sourceRoot, "agent-manifest.json"),
243
+ operation: "inspect",
244
+ reason: "Regenerate and reload the action catalog from SDK outputs after checks pass."
245
+ },
246
+ {
247
+ path: sdkOutputPath(sourceRoot, "action-registry.json"),
248
+ operation: "inspect",
249
+ reason: "Verify the default chat action and explicit actions are catalog-addressable."
250
+ }
251
+ ],
252
+ requirements: [
253
+ {
254
+ kind: "target_project",
255
+ name: "active OpenPond profile source",
256
+ status: "required",
257
+ detail: input.profile?.sourcePath ?? "Resolved from the local goal workspace.",
258
+ metadata: {}
259
+ }
260
+ ],
261
+ checks: [
262
+ { name: "inspect", command: "pnpm agent:inspect --json", required: true },
263
+ { name: "build", command: "pnpm agent:build", required: true },
264
+ { name: "validate", command: "pnpm agent:validate --json", required: true },
265
+ { name: "eval", command: "pnpm agent:eval --json", required: true }
266
+ ],
267
+ approvalId,
268
+ approvedAt: null,
269
+ editedFromPlanId: null,
270
+ metadata: {
271
+ actionShape,
272
+ actionShapeDecisionSource,
273
+ reviewSections: [
274
+ "What happened",
275
+ "Agent/workflow plan",
276
+ "Files/source to create",
277
+ "Dependencies and runtime setup",
278
+ "Secrets and connections needed",
279
+ "Validation checks"
280
+ ]
281
+ },
282
+ createdAt: now,
283
+ updatedAt: now
284
+ });
285
+ const workflowCapture = WorkflowCaptureArtifactSchema.parse({
286
+ schemaVersion: "openpond.createPipeline.workflowCapture.v1",
287
+ id: `workflow_capture_${randomUUID2()}`,
288
+ goalId: input.goal.id,
289
+ requestId,
290
+ command: input.command,
291
+ objective: input.goal.objective,
292
+ conversationExcerpts: [],
293
+ attachments: [],
294
+ apps: [],
295
+ tools: [],
296
+ profileActions: [],
297
+ externalProviders: [],
298
+ environmentVariables: [],
299
+ files: [],
300
+ schedules: [],
301
+ webhooks: [],
302
+ channelTargets: [],
303
+ outputArtifacts: [],
304
+ targetRepoAssumptions: [],
305
+ traceRefs: [],
306
+ metadata: {
307
+ capturePolicy: "Moderate capture only: names, high-level inputs/outputs, setup requirements, artifacts, and side effects. Raw secrets and large logs are excluded."
308
+ },
309
+ createdAt: now
310
+ });
311
+ const snapshot = CreatePipelineSnapshotSchema.parse({
312
+ schemaVersion: "openpond.createPipeline.snapshot.v1",
313
+ id: `create_pipeline_${randomUUID2()}`,
314
+ goalId: input.goal.id,
315
+ state: "awaiting_plan_approval",
316
+ request: parsedRequest,
317
+ plan,
318
+ workflowCapture,
319
+ approvalIds: [approvalId],
320
+ checkRefs: [],
321
+ sourceRefs: [],
322
+ localGoalId: input.goal.id,
323
+ localProfileCommit: input.profile?.localHead ?? null,
324
+ hostedGoalId: null,
325
+ hostedSourceCommit: null,
326
+ hostedSourceRef: null,
327
+ blockedReason: null,
328
+ metadata: {},
329
+ createdAt: now,
330
+ updatedAt: now
331
+ });
332
+ const approval = {
333
+ id: approvalId,
334
+ goalId: input.goal.id,
335
+ kind: "create_plan",
336
+ title: operation === "edit" ? "Approve agent edit plan" : "Approve agent create plan",
337
+ reason: "OpenPond requires plan approval before mutating durable profile agent source.",
338
+ payload: {
339
+ requestId,
340
+ planId,
341
+ pipelineId: snapshot.id,
342
+ objective: input.goal.objective,
343
+ defaultActionKey: plan.defaultChatAction.key,
344
+ sourcePlan: plan.sourcePlan,
345
+ checks: plan.checks
346
+ },
347
+ createdAt: now,
348
+ status: "pending",
349
+ decidedAt: null,
350
+ decisionNote: null
351
+ };
352
+ return { snapshot, approval };
353
+ }
354
+ function slugFromObjective(objective) {
355
+ return normalizeAgentId(
356
+ objective.toLowerCase().replace(/['"]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "created-agent"
357
+ );
358
+ }
359
+ function sourceRootPathForAgent(agentId) {
360
+ return agentId === "default" ? "agent" : `agents/${agentId}`;
361
+ }
362
+ function sdkOutputPath(sourceRoot, fileName) {
363
+ return sourceRoot === "agent" ? `.openpond/${fileName}` : `${sourceRoot}/.openpond/${fileName}`;
364
+ }
365
+ function normalizeAgentId(value) {
366
+ const normalized = value.trim().toLowerCase().replace(/['"]/g, "").replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
367
+ return normalized || "created-agent";
368
+ }
369
+ function approveCreatePipelinePlan(goal, approvalId) {
370
+ return decideCreatePipelinePlan(goal, approvalId, "approved");
371
+ }
372
+ function rejectCreatePipelinePlan(goal, approvalId, decisionNote) {
373
+ return decideCreatePipelinePlan(goal, approvalId, "rejected", decisionNote);
374
+ }
375
+ function cancelCreatePipelinePlan(goal, approvalId, decisionNote) {
376
+ return decideCreatePipelinePlan(goal, approvalId, "cancelled", decisionNote);
377
+ }
378
+ function reviseCreatePipelinePlan(goal, input) {
379
+ const pipeline = goal.createPipeline;
380
+ if (!pipeline?.plan) {
381
+ throw new Error("goal has no create plan to edit");
382
+ }
383
+ if (pipeline.plan.status !== "pending_approval") {
384
+ throw new Error("only a pending create plan can be edited before source mutation");
385
+ }
386
+ const revision = input.revision.trim();
387
+ if (!revision) {
388
+ throw new Error("plan edit instructions are required");
389
+ }
390
+ const now = (/* @__PURE__ */ new Date()).toISOString();
391
+ const previousPlan = pipeline.plan;
392
+ const nextPlan = CreatePipelinePlanSchema.parse({
393
+ ...previousPlan,
394
+ id: `create_plan_${randomUUID2()}`,
395
+ status: "pending_approval",
396
+ summary: `${previousPlan.summary}
397
+
398
+ Revision requested: ${revision}`,
399
+ sourcePlan: previousPlan.sourcePlan.map((item) => ({
400
+ ...item,
401
+ reason: `${item.reason} Revision requested: ${revision}`
402
+ })),
403
+ approvedAt: null,
404
+ editedFromPlanId: previousPlan.id,
405
+ metadata: {
406
+ ...previousPlan.metadata,
407
+ revision,
408
+ supersedesPlanId: previousPlan.id
409
+ },
410
+ createdAt: now,
411
+ updatedAt: now
412
+ });
413
+ const approvals = goal.approvals.map(
414
+ (approval) => approval.id === previousPlan.approvalId ? {
415
+ ...approval,
416
+ title: "Approve revised agent plan",
417
+ reason: "OpenPond requires approval of the revised plan before mutating durable profile agent source.",
418
+ payload: {
419
+ ...approval.payload,
420
+ planId: nextPlan.id,
421
+ previousPlanId: previousPlan.id,
422
+ objective: nextPlan.objective,
423
+ defaultActionKey: nextPlan.defaultChatAction.key,
424
+ sourcePlan: nextPlan.sourcePlan,
425
+ checks: nextPlan.checks,
426
+ revision
427
+ },
428
+ status: "pending",
429
+ decidedAt: null,
430
+ decisionNote: null
431
+ } : approval
432
+ );
433
+ return {
434
+ ...goal,
435
+ status: "awaiting_approval",
436
+ approvals,
437
+ createPipeline: CreatePipelineSnapshotSchema.parse({
438
+ ...pipeline,
439
+ state: "awaiting_plan_approval",
440
+ plan: nextPlan,
441
+ blockedReason: null,
442
+ metadata: {
443
+ ...pipeline.metadata,
444
+ previousPlanId: previousPlan.id
445
+ },
446
+ updatedAt: now
447
+ }),
448
+ updatedAt: now
449
+ };
450
+ }
451
+ function decideCreatePipelinePlan(goal, approvalId, decision, decisionNote) {
452
+ const pipeline = goal.createPipeline;
453
+ if (!pipeline?.plan || pipeline.plan.approvalId !== approvalId) return goal;
454
+ const now = (/* @__PURE__ */ new Date()).toISOString();
455
+ const nextState = decision === "approved" ? "applying_source" : decision === "cancelled" ? "cancelled" : "blocked";
456
+ return {
457
+ ...goal,
458
+ createPipeline: CreatePipelineSnapshotSchema.parse({
459
+ ...pipeline,
460
+ state: nextState,
461
+ plan: {
462
+ ...pipeline.plan,
463
+ status: decision,
464
+ approvedAt: decision === "approved" ? now : pipeline.plan.approvedAt,
465
+ metadata: {
466
+ ...pipeline.plan.metadata,
467
+ decision,
468
+ ...decisionNote ? { decisionNote } : {}
469
+ },
470
+ updatedAt: now
471
+ },
472
+ blockedReason: decision === "rejected" ? "Create plan rejected before source mutation." : decision === "cancelled" ? "Create plan cancelled before source mutation." : null,
473
+ updatedAt: now
474
+ })
475
+ };
476
+ }
477
+
478
+ // src/goal/events.ts
479
+ import { randomUUID as randomUUID3 } from "node:crypto";
480
+ function createGoalEvent(input) {
481
+ return {
482
+ id: `event_${randomUUID3()}`,
483
+ goalId: input.goalId,
484
+ iterationId: input.iterationId ?? null,
485
+ kind: input.kind,
486
+ summary: input.summary,
487
+ payload: input.payload ?? {},
488
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
489
+ };
490
+ }
491
+ function serializeGoalEventRecord(event) {
492
+ return JSON.stringify(event);
493
+ }
494
+ async function recordGoalEvent(event, targets) {
495
+ await targets.localState?.appendEvent(event.goalId, event);
496
+ await targets.hostedClient?.appendEvent(event.goalId, event);
497
+ }
498
+
499
+ // src/goal/runner.ts
500
+ import { randomUUID as randomUUID8 } from "node:crypto";
501
+
502
+ // src/goal/budgets.ts
503
+ function goalIterationBudgetRemaining(goal) {
504
+ const startedIterations = goal.events.filter(
505
+ (event) => event.kind === "iteration.started"
506
+ ).length;
507
+ return Math.max(0, goal.budget.maxIterations - startedIterations);
508
+ }
509
+
510
+ // src/goal/prompts.ts
511
+ var GENERIC_CODING_PROMPT = `# Generic Coding Goal
512
+
513
+ You are working on a source-backed coding goal. Make scoped source edits, run the configured checks, ask structured questions when blocked, and return a reviewable result.`;
514
+ var CREATE_AGENT_PROMPT = `# Create OpenPond Agent Goal
515
+
516
+ Create a source-backed OpenPond agent from the user's prompt. Ask structured questions for missing setup decisions, run the project-local OpenPond agent SDK commands through the openpond_agent_* tools, and leave a reviewable source update.
517
+
518
+ Do not invoke openpond-agent through shell, npx, pnpm dlx, or yarn dlx.
519
+
520
+ Create-agent and edit-agent Goals use a create pipeline. The pipeline request, plan, and workflow capture are durable Goal state under the configured local Goal storage root, normally ~/.openpond/goals/<goalId>; they are operational run history, not profile source to commit. Source mutation is allowed only after the create_plan approval is approved. When the Goal metadata contains an approved create pipeline plan, follow that plan, preserve or create a default chat action, keep openpond_chat routed to that default action, and run the required SDK checks before claiming the agent is ready.
521
+
522
+ ## Create Plan Action Shape
523
+
524
+ When you author or revise a create plan, decide the public action shape from the user request, captured conversation context, attachments, prior tool/action evidence, existing profile catalog, and answered questions. Do not use hardcoded business examples or hidden keyword lists.
525
+
526
+ Record the decision as metadata.actionShape on the create plan:
527
+
528
+ {
529
+ "mode": "chat | direct_action | chat_and_direct_actions",
530
+ "label": "Short user-visible shape label",
531
+ "detail": "Why this shape fits the request",
532
+ "defaultActionKey": "chat or the generated default chat action id",
533
+ "directActionHint": "The repeatable direct action to expose, or null",
534
+ "artifactPolicy": "What run summaries, traces, and output artifacts should be persisted"
535
+ }
536
+
537
+ Use chat when the request is mainly conversational: answer questions, help with analysis, route follow-ups, or operate through normal OpenPond chat. Use direct_action when the request is a repeatable runnable operation with clear inputs and outputs, such as producing a report, transforming an artifact, running a workflow, exporting/importing data, or triggering a deterministic task without needing a conversation. Use chat_and_direct_actions when both a normal chat surface and one or more repeatable catalog actions are useful. If the source data, setup, output artifact, or invocation shape is ambiguous enough to change source design, ask a focused questions_ask question before requesting approval instead of guessing.
538
+
539
+ If an existing plan says metadata.actionShapeDecisionSource is default_chat_fallback, treat that shape as a conservative app fallback rather than a model decision; refine the generated actions from the approved objective and captured context when the requested behavior clearly needs a direct action.
540
+
541
+ Hosted create-agent goals may start from a generated OpenPond SDK project with package.json, openpond.yaml, and agent/** already present. Treat that as the scaffold to edit. Do not exhaust tool rounds reading every source file. Once you understand the scaffold and answered questions, update the minimal source files needed, run openpond_agent_default_checks, and then return a final no-tool response. If checks fail, use the check output and trace refs as repair input, edit the source or eval that is wrong, and rerun checks before returning a final response. Generated evals must test the behavior the agent actually implements; if you add a new intent/action, wire the default chat path or the eval input so the intent and result contract match.
542
+
543
+ Do not inspect the openpond-agent-sdk package internals, vendored SDK source, or generated .openpond outputs unless an OpenPond agent check explicitly reports an error in those files. Use the existing agent/** files and the openpond_agent_* tools as the contract.
544
+
545
+ For SharePoint or other unavailable external systems, do not spend multiple rounds searching for CLIs, SDK packages, credentials, or network access. If the user gives enough target details but real external access is unavailable, create a local draft agent behavior that detects the request, explains the missing external access honestly, and includes eval coverage for that intent. Then run openpond_agent_default_checks and return a final answer with the external access blocker. Do not claim that an external read/write succeeded.`;
546
+ var UPDATE_AGENT_PROMPT = `# Update OpenPond Agent Goal
547
+
548
+ Update an existing source-backed OpenPond agent from the user's request and evidence refs. Run before/after checks through the openpond_agent_* tools and leave a reviewable source update. Do not invoke openpond-agent through shell, npx, pnpm dlx, or yarn dlx.`;
549
+ function resolveGoalPromptPack(goal) {
550
+ if (goal.promptPack === "openpond_agent_create_v1") {
551
+ return {
552
+ id: goal.promptPack,
553
+ title: "Create OpenPond Agent Goal",
554
+ instructions: CREATE_AGENT_PROMPT
555
+ };
556
+ }
557
+ if (goal.promptPack === "openpond_agent_update_v1") {
558
+ return {
559
+ id: goal.promptPack,
560
+ title: "Update OpenPond Agent Goal",
561
+ instructions: UPDATE_AGENT_PROMPT
562
+ };
563
+ }
564
+ return {
565
+ id: goal.promptPack || "generic_coding_v1",
566
+ title: "Generic Coding Goal",
567
+ instructions: GENERIC_CODING_PROMPT
568
+ };
569
+ }
570
+
571
+ // src/goal/redaction.ts
572
+ var SECRET_PATTERNS = [
573
+ /\bopk_[A-Za-z0-9_-]{12,}\b/g,
574
+ /\bsk-[A-Za-z0-9_-]{12,}\b/g,
575
+ /\bghp_[A-Za-z0-9_]{12,}\b/g,
576
+ /\bgithub_pat_[A-Za-z0-9_]{12,}\b/g,
577
+ /\bOPENPOND_[A-Z0-9_]*(?:API_KEY|TOKEN|SECRET)\s*=\s*[^\s]+/g,
578
+ /\b(?:API_KEY|TOKEN|SECRET|PASSWORD)\s*=\s*[^\s]+/gi
579
+ ];
580
+ var REDACTED = "[redacted]";
581
+ function redactString(value) {
582
+ return SECRET_PATTERNS.reduce(
583
+ (current, pattern) => current.replace(pattern, REDACTED),
584
+ value
585
+ );
586
+ }
587
+ function truncateForEvent(value, maxLength = 4e3) {
588
+ const redacted = redactString(value);
589
+ if (redacted.length <= maxLength) return redacted;
590
+ return `${redacted.slice(0, maxLength)}
591
+ [truncated ${redacted.length - maxLength} chars]`;
592
+ }
593
+ function redactJson(value) {
594
+ if (typeof value === "string") return redactString(value);
595
+ if (Array.isArray(value)) return value.map((entry) => redactJson(entry));
596
+ if (!value || typeof value !== "object") return value;
597
+ return Object.fromEntries(
598
+ Object.entries(value).map(([key, entry]) => [
599
+ key,
600
+ redactJson(entry)
601
+ ])
602
+ );
603
+ }
604
+
605
+ // src/goal/llm.ts
606
+ function buildGoalLlmMessages(goal) {
607
+ const promptPack = resolveGoalPromptPack(goal);
608
+ return [
609
+ {
610
+ role: "system",
611
+ content: [
612
+ "You are the OpenPond Goal runner. Produce executable tool calls, ask structured questions when blocked, and stay within the provided execution policy.",
613
+ "",
614
+ promptPack.instructions,
615
+ "",
616
+ "Runner rules:",
617
+ "- Use files_write for source edits instead of describing edits.",
618
+ "- Use questions_ask for required user input.",
619
+ "- Use checks_run or configured verification commands before claiming completion.",
620
+ "- Hosted coding sandboxes include node, npm, pnpm, and common POSIX utilities; follow the target repository's lockfile when choosing install/test commands, use command -v for PATH checks, and do not block on standalone toolchain probes once a needed package manager command works.",
621
+ "- For OpenPond agent goals, run SDK actions through openpond_agent_* tools. Do not invoke openpond-agent through shell, npx, pnpm dlx, or yarn dlx.",
622
+ "- For OpenPond agent goals, after source edits use openpond_agent_default_checks unless a narrower SDK command is clearly enough; do not spend the remaining rounds on broad inspection.",
623
+ "- After required edits and checks are complete, return a concise final response with no tool calls.",
624
+ "- Do not keep inspecting once you have enough context to make the requested change.",
625
+ "- Do not claim external integration reads or writes succeeded without tool evidence."
626
+ ].join("\n")
627
+ },
628
+ {
629
+ role: "user",
630
+ content: buildGoalUserContext(goal)
631
+ }
632
+ ];
633
+ }
634
+ function buildGoalUserContext(goal) {
635
+ const answeredQuestions = goal.questions.filter((question) => question.answeredAt).map((question) => {
636
+ const answer = goal.answers.find((item) => item.questionId === question.id);
637
+ return {
638
+ question: question.title,
639
+ optionId: answer?.optionId ?? null,
640
+ answer: answer?.freeformText ?? null,
641
+ value: answer?.value ?? {}
642
+ };
643
+ });
644
+ const openQuestions = goal.questions.filter((question) => !question.answeredAt).map((question) => ({
645
+ title: question.title,
646
+ reason: question.reason,
647
+ required: question.required,
648
+ options: question.options,
649
+ freeformAllowed: question.freeformAllowed
650
+ }));
651
+ const approvals = goal.approvals.map((approval) => ({
652
+ kind: approval.kind,
653
+ status: approval.status,
654
+ title: approval.title,
655
+ reason: approval.reason,
656
+ decisionNote: approval.decisionNote,
657
+ decidedAt: approval.decidedAt
658
+ }));
659
+ const createPipeline = goal.createPipeline ? {
660
+ id: goal.createPipeline.id,
661
+ state: goal.createPipeline.state,
662
+ request: {
663
+ id: goal.createPipeline.request.id,
664
+ operation: goal.createPipeline.request.operation,
665
+ surface: goal.createPipeline.request.surface,
666
+ command: goal.createPipeline.request.command,
667
+ sourceAuthority: goal.createPipeline.request.adapter.sourceAuthority,
668
+ confirmationPolicy: goal.createPipeline.request.adapter.confirmationPolicy
669
+ },
670
+ plan: goal.createPipeline.plan ? {
671
+ id: goal.createPipeline.plan.id,
672
+ status: goal.createPipeline.plan.status,
673
+ summary: goal.createPipeline.plan.summary,
674
+ defaultChatAction: goal.createPipeline.plan.defaultChatAction,
675
+ sourcePlan: goal.createPipeline.plan.sourcePlan,
676
+ requirements: goal.createPipeline.plan.requirements,
677
+ checks: goal.createPipeline.plan.checks,
678
+ metadata: {
679
+ actionShape: goal.createPipeline.plan.metadata?.actionShape ?? null,
680
+ actionShapeDecisionSource: goal.createPipeline.plan.metadata?.actionShapeDecisionSource ?? null
681
+ },
682
+ approvedAt: goal.createPipeline.plan.approvedAt
683
+ } : null,
684
+ workflowCapture: goal.createPipeline.workflowCapture ? {
685
+ id: goal.createPipeline.workflowCapture.id,
686
+ tools: goal.createPipeline.workflowCapture.tools,
687
+ apps: goal.createPipeline.workflowCapture.apps,
688
+ targetRepoAssumptions: goal.createPipeline.workflowCapture.targetRepoAssumptions
689
+ } : null
690
+ } : null;
691
+ return [
692
+ `Objective: ${goal.objective}`,
693
+ "",
694
+ "Goal metadata:",
695
+ JSON.stringify(
696
+ {
697
+ goalId: goal.id,
698
+ kind: goal.kind,
699
+ profile: goal.profile,
700
+ promptPack: goal.promptPack,
701
+ status: goal.status,
702
+ constraints: goal.constraints,
703
+ evidenceRefs: goal.evidenceRefs,
704
+ executionPolicy: goal.executionPolicy,
705
+ verification: goal.verification,
706
+ createPipeline,
707
+ answeredQuestions,
708
+ openQuestions,
709
+ approvals
710
+ },
711
+ null,
712
+ 2
713
+ )
714
+ ].join("\n");
715
+ }
716
+ async function callHostedGoalLlm(params) {
717
+ const request = {
718
+ goalId: params.goal.id,
719
+ iterationId: params.iterationId,
720
+ promptPack: params.goal.promptPack,
721
+ messages: params.messages ?? buildGoalLlmMessages(params.goal),
722
+ tools: params.tools,
723
+ metadata: params.metadata
724
+ };
725
+ const response = await params.hostedClient.callLlm(request);
726
+ if (response.usage) {
727
+ await recordGoalEvent(
728
+ createGoalEvent({
729
+ goalId: params.goal.id,
730
+ iterationId: params.iterationId,
731
+ kind: "model.usage",
732
+ summary: "Goal LLM call completed",
733
+ payload: redactJson({ usage: response.usage })
734
+ }),
735
+ { localState: params.localState, hostedClient: params.hostedClient }
736
+ );
737
+ }
738
+ return response;
739
+ }
740
+ function resolveLocalGoalLlmConfig(env = typeof process === "undefined" ? {} : process.env) {
741
+ const token = env.OPENPOND_GOAL_OPCHAT_API_KEY?.trim() || env.OPENPOND_OPCHAT_API_KEY?.trim() || env.OPENPOND_API_KEY?.trim();
742
+ if (!token) return null;
743
+ return {
744
+ apiBaseUrl: resolveOpChatApiBaseUrl({ env }),
745
+ token,
746
+ model: env.OPENPOND_GOAL_MODEL?.trim() || env.OPENPOND_OPCHAT_MODEL?.trim() || "openpond-chat"
747
+ };
748
+ }
749
+ async function callLocalGoalLlm(params) {
750
+ const config = resolveLocalGoalLlmConfig(params.env);
751
+ if (!config) return null;
752
+ const completion = await sendHostedChatTurn({
753
+ apiBaseUrl: config.apiBaseUrl,
754
+ token: config.token,
755
+ model: config.model,
756
+ messages: params.messages ?? buildGoalLlmMessages(params.goal),
757
+ tools: params.tools,
758
+ toolChoice: params.tools?.length ? "auto" : void 0,
759
+ temperature: 0.2,
760
+ metadata: {
761
+ source: "openpond-code-goal",
762
+ goalId: params.goal.id,
763
+ promptPack: params.goal.promptPack,
764
+ ...params.metadata ?? {}
765
+ }
766
+ });
767
+ const response = goalLlmResponseFromHostedChatCompletion(completion, {
768
+ afterToolResults: (params.messages ?? []).some(
769
+ (message) => message.role === "tool"
770
+ )
771
+ });
772
+ if (response.usage) {
773
+ await recordGoalEvent(
774
+ createGoalEvent({
775
+ goalId: params.goal.id,
776
+ iterationId: params.iterationId,
777
+ kind: "model.usage",
778
+ summary: "Local Goal LLM call completed",
779
+ payload: redactJson({ usage: response.usage })
780
+ }),
781
+ { localState: params.localState }
782
+ );
783
+ }
784
+ return response;
785
+ }
786
+ function goalLlmResponseFromHostedChatCompletion(completion, options = {}) {
787
+ const choice = completion.choices?.[0];
788
+ const message = choice?.message;
789
+ const content = typeof message?.content === "string" ? message.content.trim() : "";
790
+ const toolCalls = (message?.tool_calls ?? []).flatMap((toolCall) => {
791
+ const id = typeof toolCall.id === "string" ? toolCall.id : "";
792
+ const name = typeof toolCall.function?.name === "string" ? toolCall.function.name : "";
793
+ if (!id || !name) return [];
794
+ return [
795
+ {
796
+ id,
797
+ name,
798
+ arguments: parseToolArguments(toolCall.function?.arguments)
799
+ }
800
+ ];
801
+ });
802
+ return {
803
+ status: toolCalls.length > 0 || options.afterToolResults ? "ok" : "blocked",
804
+ summary: toolCalls.length > 0 ? `Goal planner returned ${toolCalls.length} tool call${toolCalls.length === 1 ? "" : "s"}.` : content || "Goal planner returned no executable tool calls.",
805
+ message: {
806
+ role: "assistant",
807
+ content
808
+ },
809
+ ...toolCalls.length > 0 ? { toolCalls } : {},
810
+ ...completion.usage ? {
811
+ usage: {
812
+ inputTokens: completion.usage.prompt_tokens ?? completion.usage.input_tokens,
813
+ outputTokens: completion.usage.completion_tokens ?? completion.usage.output_tokens,
814
+ totalTokens: completion.usage.total_tokens
815
+ }
816
+ } : {}
817
+ };
818
+ }
819
+ function parseToolArguments(value) {
820
+ if (value && typeof value === "object" && !Array.isArray(value)) {
821
+ return value;
822
+ }
823
+ if (typeof value !== "string" || !value.trim()) return {};
824
+ try {
825
+ const parsed = JSON.parse(value);
826
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
827
+ } catch {
828
+ return {};
829
+ }
830
+ }
831
+
832
+ // src/goal/result.ts
833
+ function createGoalRunResult(input) {
834
+ return {
835
+ goalId: input.goal.id,
836
+ status: input.status,
837
+ summary: input.summary,
838
+ events: input.events
839
+ };
840
+ }
841
+
842
+ // src/goal/local-create-pipeline.ts
843
+ var MODEL_BACKED_LOCAL_CREATE_REQUIRED_REASON = "Approved local Create plans require model-backed SDK source application; no source mutation was performed.";
844
+ function shouldRunLocalCreatePipeline(goal, mode) {
845
+ const pipeline = goal.createPipeline;
846
+ return Boolean(
847
+ mode === "local" && pipeline && pipeline.state === "applying_source" && pipeline.request.adapter.kind === "local"
848
+ );
849
+ }
850
+ async function runLocalCreatePipeline(input) {
851
+ const localState = input.localState;
852
+ if (!localState) {
853
+ const blocked2 = createGoalEvent({
854
+ goalId: input.goal.id,
855
+ iterationId: input.iterationId,
856
+ kind: "goal.blocked",
857
+ summary: "Local create pipeline requires local Goal state",
858
+ payload: { reason: "missing_local_state" }
859
+ });
860
+ return createGoalRunResult({
861
+ goal: input.goal,
862
+ status: "blocked",
863
+ summary: blocked2.summary,
864
+ events: [...input.startedEvents, blocked2]
865
+ });
866
+ }
867
+ const current = await localState.get(input.goal.id) ?? input.goal;
868
+ const pipeline = current.createPipeline;
869
+ if (!pipeline || pipeline.request.adapter.kind !== "local") {
870
+ const blocked2 = createGoalEvent({
871
+ goalId: current.id,
872
+ iterationId: input.iterationId,
873
+ kind: "goal.blocked",
874
+ summary: "Goal does not have a local create pipeline to run",
875
+ payload: { reason: "missing_local_create_pipeline" }
876
+ });
877
+ await recordGoalEvent(blocked2, { localState });
878
+ await localState.update({ ...current, status: "blocked" });
879
+ return createGoalRunResult({
880
+ goal: current,
881
+ status: "blocked",
882
+ summary: blocked2.summary,
883
+ events: [...input.startedEvents, blocked2]
884
+ });
885
+ }
886
+ const events = [...input.startedEvents];
887
+ const blockedTransition = await transitionLocalCreatePipeline({
888
+ localState,
889
+ goalId: current.id,
890
+ iterationId: input.iterationId,
891
+ state: "blocked",
892
+ summary: MODEL_BACKED_LOCAL_CREATE_REQUIRED_REASON,
893
+ blockedReason: MODEL_BACKED_LOCAL_CREATE_REQUIRED_REASON
894
+ });
895
+ events.push(blockedTransition.event);
896
+ await localState.update({
897
+ ...blockedTransition.goal,
898
+ status: "blocked"
899
+ });
900
+ const blocked = createGoalEvent({
901
+ goalId: current.id,
902
+ iterationId: input.iterationId,
903
+ kind: "goal.blocked",
904
+ summary: MODEL_BACKED_LOCAL_CREATE_REQUIRED_REASON,
905
+ payload: {
906
+ pipelineId: pipeline.id,
907
+ operation: pipeline.request.operation,
908
+ reason: "model_backed_source_application_required",
909
+ sourcePath: pipeline.request.adapter.sourcePath ?? null,
910
+ repoPath: pipeline.request.adapter.repoPath ?? null
911
+ }
912
+ });
913
+ await recordGoalEvent(blocked, { localState });
914
+ events.push(blocked);
915
+ return createGoalRunResult({
916
+ goal: blockedTransition.goal,
917
+ status: "blocked",
918
+ summary: blocked.summary,
919
+ events
920
+ });
921
+ }
922
+ async function transitionLocalCreatePipeline(input) {
923
+ const current = await input.localState.get(input.goalId);
924
+ if (!current?.createPipeline) {
925
+ throw new Error(`goal has no create pipeline: ${input.goalId}`);
926
+ }
927
+ const now = (/* @__PURE__ */ new Date()).toISOString();
928
+ const previousState = current.createPipeline.state;
929
+ const nextPipeline = CreatePipelineSnapshotSchema.parse({
930
+ ...current.createPipeline,
931
+ state: input.state,
932
+ checkRefs: mergeRefs(current.createPipeline.checkRefs, input.checkRefs ?? []),
933
+ blockedReason: input.blockedReason === void 0 ? current.createPipeline.blockedReason : input.blockedReason,
934
+ updatedAt: now
935
+ });
936
+ const goal = await input.localState.update({
937
+ ...current,
938
+ createPipeline: nextPipeline,
939
+ updatedAt: now
940
+ });
941
+ const event = createGoalEvent({
942
+ goalId: input.goalId,
943
+ iterationId: input.iterationId,
944
+ kind: "create_pipeline.status_changed",
945
+ summary: input.summary,
946
+ payload: {
947
+ fromState: previousState,
948
+ toState: input.state,
949
+ pipelineId: nextPipeline.id,
950
+ blockedReason: nextPipeline.blockedReason,
951
+ checkRefs: nextPipeline.checkRefs
952
+ }
953
+ });
954
+ await recordGoalEvent(event, { localState: input.localState });
955
+ return {
956
+ goal: await input.localState.get(input.goalId) ?? goal,
957
+ event
958
+ };
959
+ }
960
+ function mergeRefs(existing, next) {
961
+ return Array.from(new Set([...existing, ...next].filter(Boolean)));
962
+ }
963
+
964
+ // src/goal/policy.ts
965
+ function assertGoalCanStartIteration(goal) {
966
+ if (goal.status === "cancelled" || goal.status === "completed" || goal.status === "failed") {
967
+ throw new Error(`goal is terminal: ${goal.status}`);
968
+ }
969
+ if (goal.status === "paused") throw new Error("goal is paused");
970
+ if (goal.status === "awaiting_user_input") {
971
+ throw new Error("goal is awaiting required user input");
972
+ }
973
+ if (goal.status === "awaiting_approval") {
974
+ throw new Error("goal is awaiting approval");
975
+ }
976
+ }
977
+
978
+ // src/goal/profiles/generic-coding.ts
979
+ var GENERIC_CODING_PROFILE = {
980
+ id: "generic_coding",
981
+ promptPack: "generic_coding_v1",
982
+ defaultVerificationCommands: [],
983
+ toolNames: [
984
+ "shell",
985
+ "files",
986
+ "checks",
987
+ "artifacts",
988
+ "questions",
989
+ "approvals",
990
+ "source"
991
+ ]
992
+ };
993
+
994
+ // src/goal/profiles/openpond-agent.ts
995
+ var OPENPOND_AGENT_PROFILE = {
996
+ id: "openpond_agent",
997
+ promptPack: "openpond_agent_create_v1",
998
+ defaultVerificationCommands: [],
999
+ toolNames: [
1000
+ "shell",
1001
+ "files",
1002
+ "checks",
1003
+ "artifacts",
1004
+ "questions",
1005
+ "approvals",
1006
+ "source",
1007
+ "openpond_agent_sdk"
1008
+ ]
1009
+ };
1010
+
1011
+ // src/goal/profiles/index.ts
1012
+ function getGoalProfileDescriptor(goal) {
1013
+ if (goal.profile === "openpond_agent") {
1014
+ return {
1015
+ ...OPENPOND_AGENT_PROFILE,
1016
+ promptPack: goal.promptPack
1017
+ };
1018
+ }
1019
+ return {
1020
+ ...GENERIC_CODING_PROFILE,
1021
+ promptPack: goal.promptPack
1022
+ };
1023
+ }
1024
+
1025
+ // src/goal/tools/checks.ts
1026
+ import { randomUUID as randomUUID5 } from "node:crypto";
1027
+
1028
+ // src/goal/limits.ts
1029
+ import { Buffer } from "node:buffer";
1030
+ var MAX_GOAL_FILE_READ_BYTES = 1024 * 1024;
1031
+ var MAX_GOAL_COMMAND_LOG_ARTIFACT_BYTES = 256 * 1024;
1032
+ function assertGoalFileReadSize(path, size) {
1033
+ if (size <= MAX_GOAL_FILE_READ_BYTES) return;
1034
+ throw new Error(
1035
+ `file is too large to read through goal tools: ${path} (${size} bytes, max ${MAX_GOAL_FILE_READ_BYTES} bytes)`
1036
+ );
1037
+ }
1038
+ function truncateGoalTextArtifact(content, maxBytes = MAX_GOAL_COMMAND_LOG_ARTIFACT_BYTES) {
1039
+ if (Buffer.byteLength(content, "utf-8") <= maxBytes) {
1040
+ return { content, truncated: false };
1041
+ }
1042
+ const marker = `
1043
+ [truncated after ${maxBytes} bytes]
1044
+ `;
1045
+ const bodyBytes = Math.max(0, maxBytes - Buffer.byteLength(marker, "utf-8"));
1046
+ return {
1047
+ content: `${Buffer.from(content).subarray(0, bodyBytes).toString("utf8")}${marker}`,
1048
+ truncated: true
1049
+ };
1050
+ }
1051
+
1052
+ // src/goal/tools/artifacts.ts
1053
+ import { Buffer as Buffer2 } from "node:buffer";
1054
+ import { randomUUID as randomUUID4 } from "node:crypto";
1055
+ import { mkdir, writeFile } from "node:fs/promises";
1056
+ import os2 from "node:os";
1057
+ import { join as join2 } from "node:path";
1058
+ function safeFileName(value) {
1059
+ return value.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "artifact";
1060
+ }
1061
+ function createGoalArtifact(input) {
1062
+ const content = redactString(input.content);
1063
+ return {
1064
+ id: `artifact_${randomUUID4()}`,
1065
+ goalId: input.goalId,
1066
+ iterationId: input.iterationId ?? null,
1067
+ kind: input.kind,
1068
+ name: input.name,
1069
+ mimeType: input.mimeType ?? "text/plain",
1070
+ content,
1071
+ bytes: Buffer2.byteLength(content, "utf-8"),
1072
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1073
+ };
1074
+ }
1075
+ async function recordGoalArtifact(params) {
1076
+ const artifact = params.artifact;
1077
+ const ref = params.hostedClient ? await params.hostedClient.uploadArtifact(artifact) : await writeLocalArtifact(artifact, params.storageRoot ?? params.workspace);
1078
+ await recordGoalEvent(
1079
+ createGoalEvent({
1080
+ goalId: artifact.goalId,
1081
+ iterationId: artifact.iterationId,
1082
+ kind: "artifact.created",
1083
+ summary: `Artifact created: ${artifact.name}`,
1084
+ payload: {
1085
+ artifactRef: ref.ref,
1086
+ artifactId: ref.id,
1087
+ kind: ref.kind,
1088
+ name: ref.name,
1089
+ bytes: ref.bytes
1090
+ }
1091
+ }),
1092
+ { localState: params.localState, hostedClient: params.hostedClient }
1093
+ );
1094
+ return ref;
1095
+ }
1096
+ async function writeLocalArtifact(artifact, storageRoot) {
1097
+ const root = storageRoot || process.cwd();
1098
+ const dir = join2(root, ".openpond", "goals", artifact.goalId, "artifacts");
1099
+ await mkdir(dir, { recursive: true });
1100
+ const fileName = `${artifact.id}-${safeFileName(artifact.name)}`;
1101
+ const path = join2(dir, fileName);
1102
+ await writeFile(path, artifact.content, "utf-8");
1103
+ const relativeRef = `.openpond/goals/${artifact.goalId}/artifacts/${fileName}`;
1104
+ return {
1105
+ id: artifact.id,
1106
+ ref: root === os2.homedir() ? `~/${relativeRef}` : relativeRef,
1107
+ kind: artifact.kind,
1108
+ name: artifact.name,
1109
+ mimeType: artifact.mimeType,
1110
+ bytes: artifact.bytes
1111
+ };
1112
+ }
1113
+
1114
+ // src/goal/tools/shell.ts
1115
+ var DEFAULT_GOAL_SHELL_TIMEOUT_SECONDS = 120;
1116
+ async function runGoalShellCommand(params) {
1117
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
1118
+ const redactedCommand = redactString(params.command);
1119
+ await recordGoalEvent(
1120
+ createGoalEvent({
1121
+ goalId: params.goalId,
1122
+ iterationId: params.iterationId,
1123
+ kind: "command.started",
1124
+ summary: `Command started: ${redactedCommand}`,
1125
+ payload: { command: redactedCommand, cwd: params.cwd }
1126
+ }),
1127
+ { localState: params.localState, hostedClient: params.hostedClient }
1128
+ );
1129
+ const start = Date.now();
1130
+ const result = await runShellCommand(params.command, {
1131
+ cwd: params.cwd,
1132
+ env: params.env,
1133
+ timeoutSeconds: params.timeoutSeconds ?? DEFAULT_GOAL_SHELL_TIMEOUT_SECONDS
1134
+ });
1135
+ const completedAt = (/* @__PURE__ */ new Date()).toISOString();
1136
+ const artifactRefs = [];
1137
+ const commandResult = {
1138
+ command: params.command,
1139
+ cwd: params.cwd,
1140
+ code: result.code,
1141
+ timedOut: result.timedOut,
1142
+ durationMs: Date.now() - start,
1143
+ stdoutTail: truncateForEvent(result.stdout),
1144
+ stderrTail: truncateForEvent(result.stderr),
1145
+ artifactRefs,
1146
+ startedAt,
1147
+ completedAt
1148
+ };
1149
+ const combinedOutput = [
1150
+ result.stdout ? `stdout:
1151
+ ${result.stdout}` : "",
1152
+ result.stderr ? `stderr:
1153
+ ${result.stderr}` : ""
1154
+ ].filter(Boolean).join("\n\n");
1155
+ if (combinedOutput.trim()) {
1156
+ const artifactContent = truncateGoalTextArtifact(redactString(combinedOutput));
1157
+ const artifact = createGoalArtifact({
1158
+ goalId: params.goalId,
1159
+ iterationId: params.iterationId,
1160
+ kind: "command_log",
1161
+ name: `${redactedCommand.replace(/\s+/g, "_").slice(0, 80)}.log`,
1162
+ content: artifactContent.content
1163
+ });
1164
+ const ref = await recordGoalArtifact({
1165
+ artifact,
1166
+ workspace: params.workspace,
1167
+ storageRoot: params.storageRoot,
1168
+ localState: params.localState,
1169
+ hostedClient: params.hostedClient
1170
+ });
1171
+ artifactRefs.push(ref.ref);
1172
+ }
1173
+ await recordGoalEvent(
1174
+ createGoalEvent({
1175
+ goalId: params.goalId,
1176
+ iterationId: params.iterationId,
1177
+ kind: "command.completed",
1178
+ summary: result.code === 0 ? `Command completed: ${redactedCommand}` : `Command failed: ${redactedCommand}`,
1179
+ payload: {
1180
+ ...commandResult,
1181
+ command: redactedCommand,
1182
+ artifactRefs
1183
+ }
1184
+ }),
1185
+ { localState: params.localState, hostedClient: params.hostedClient }
1186
+ );
1187
+ return commandResult;
1188
+ }
1189
+
1190
+ // src/goal/tools/checks.ts
1191
+ async function runGoalVerificationChecks(params) {
1192
+ const checks = [];
1193
+ for (const command of params.goal.verification.commands) {
1194
+ const redactedCommand = redactString(command);
1195
+ const result = await runGoalShellCommand({
1196
+ goalId: params.goal.id,
1197
+ iterationId: params.iterationId,
1198
+ command,
1199
+ cwd: params.cwd,
1200
+ workspace: params.workspace,
1201
+ storageRoot: params.storageRoot,
1202
+ localState: params.localState,
1203
+ hostedClient: params.hostedClient
1204
+ });
1205
+ const status = result.code === 0 && !result.timedOut ? "passed" : "failed";
1206
+ const check = {
1207
+ id: `check_${randomUUID5()}`,
1208
+ goalId: params.goal.id,
1209
+ iterationId: params.iterationId,
1210
+ name: redactedCommand,
1211
+ command: redactedCommand,
1212
+ status,
1213
+ code: result.code,
1214
+ timedOut: result.timedOut,
1215
+ durationMs: result.durationMs,
1216
+ summary: status === "passed" ? `Check passed: ${redactedCommand}` : `Check failed: ${redactedCommand}`,
1217
+ artifactRefs: result.artifactRefs,
1218
+ startedAt: result.startedAt,
1219
+ completedAt: result.completedAt
1220
+ };
1221
+ checks.push(check);
1222
+ await recordGoalEvent(
1223
+ createGoalEvent({
1224
+ goalId: params.goal.id,
1225
+ iterationId: params.iterationId,
1226
+ kind: "check.completed",
1227
+ summary: check.summary,
1228
+ payload: check
1229
+ }),
1230
+ { localState: params.localState, hostedClient: params.hostedClient }
1231
+ );
1232
+ if (status !== "passed") break;
1233
+ }
1234
+ return checks;
1235
+ }
1236
+
1237
+ // src/goal/tools/registry.ts
1238
+ var OPENPOND_AGENT_SDK_COMMANDS = [
1239
+ "inspect",
1240
+ "build",
1241
+ "validate",
1242
+ "eval",
1243
+ "traces"
1244
+ ];
1245
+ var GOAL_TOOL_REGISTRY = [
1246
+ {
1247
+ family: "files",
1248
+ canonicalName: "files.read",
1249
+ aliases: ["files_read", "files.read"],
1250
+ schema: tool(
1251
+ "files_read",
1252
+ "Read a UTF-8 file from the goal workspace.",
1253
+ {
1254
+ path: stringSchema("Workspace-relative path to read.")
1255
+ },
1256
+ ["path"]
1257
+ )
1258
+ },
1259
+ {
1260
+ family: "files",
1261
+ canonicalName: "files.write",
1262
+ aliases: ["files_write", "files.write"],
1263
+ schema: tool(
1264
+ "files_write",
1265
+ "Write a UTF-8 file inside the goal workspace.",
1266
+ {
1267
+ path: stringSchema("Workspace-relative path to write."),
1268
+ content: stringSchema("Complete file content to write.")
1269
+ },
1270
+ ["path", "content"]
1271
+ )
1272
+ },
1273
+ {
1274
+ family: "files",
1275
+ canonicalName: "files.list",
1276
+ aliases: ["files_list", "files.list"],
1277
+ schema: tool(
1278
+ "files_list",
1279
+ "List direct children of a workspace directory.",
1280
+ {
1281
+ path: {
1282
+ ...stringSchema("Workspace-relative directory path. Defaults to root."),
1283
+ default: "."
1284
+ }
1285
+ }
1286
+ )
1287
+ },
1288
+ {
1289
+ family: "shell",
1290
+ canonicalName: "shell.run",
1291
+ aliases: ["shell", "shell_run", "shell.run"],
1292
+ schema: tool(
1293
+ "shell_run",
1294
+ "Run a shell command inside the goal workspace.",
1295
+ {
1296
+ command: stringSchema("Command to execute."),
1297
+ cwd: {
1298
+ ...stringSchema("Workspace-relative directory. Defaults to root."),
1299
+ default: "."
1300
+ },
1301
+ timeoutSeconds: {
1302
+ type: "number",
1303
+ minimum: 1,
1304
+ maximum: 600,
1305
+ description: "Optional command timeout in seconds."
1306
+ }
1307
+ },
1308
+ ["command"]
1309
+ )
1310
+ },
1311
+ {
1312
+ family: "questions",
1313
+ canonicalName: "questions.ask",
1314
+ aliases: ["questions", "questions_ask", "questions.ask"],
1315
+ schema: tool(
1316
+ "questions_ask",
1317
+ "Ask a structured user question and pause if required.",
1318
+ {
1319
+ title: stringSchema("Short question title."),
1320
+ reason: stringSchema("Why the answer is needed."),
1321
+ required: {
1322
+ type: "boolean",
1323
+ description: "Whether work must pause until answered.",
1324
+ default: true
1325
+ },
1326
+ freeformAllowed: {
1327
+ type: "boolean",
1328
+ description: "Whether the user may type a custom answer.",
1329
+ default: true
1330
+ },
1331
+ options: {
1332
+ type: "array",
1333
+ description: "Optional answer choices.",
1334
+ items: {
1335
+ type: "object",
1336
+ properties: {
1337
+ id: stringSchema("Stable option id."),
1338
+ label: stringSchema("User-visible option label."),
1339
+ description: stringSchema("Optional option detail.")
1340
+ },
1341
+ required: ["id", "label"],
1342
+ additionalProperties: false
1343
+ }
1344
+ }
1345
+ },
1346
+ ["title", "reason"]
1347
+ )
1348
+ },
1349
+ {
1350
+ family: "approvals",
1351
+ canonicalName: "approvals.request",
1352
+ aliases: [
1353
+ "approvals",
1354
+ "approval_request",
1355
+ "approvals_request",
1356
+ "approvals.request"
1357
+ ],
1358
+ schema: tool(
1359
+ "approvals_request",
1360
+ "Request user approval for publish, integration writes, secret/env changes, budget escalation, or other external side effects.",
1361
+ {
1362
+ kind: {
1363
+ type: "string",
1364
+ enum: [
1365
+ "create_plan",
1366
+ "deploy_publish",
1367
+ "integration_write",
1368
+ "secret_or_env_change",
1369
+ "budget_escalation",
1370
+ "external_effect"
1371
+ ],
1372
+ description: "The type of approval gate needed."
1373
+ },
1374
+ title: stringSchema("Short approval title shown to the user."),
1375
+ reason: stringSchema("Why approval is required before continuing."),
1376
+ payload: {
1377
+ type: "object",
1378
+ description: "Optional compact approval metadata.",
1379
+ additionalProperties: true
1380
+ }
1381
+ },
1382
+ ["kind", "title", "reason"]
1383
+ )
1384
+ },
1385
+ {
1386
+ family: "checks",
1387
+ canonicalName: "checks.run",
1388
+ aliases: ["checks", "checks_run", "checks.run"],
1389
+ schema: tool(
1390
+ "checks_run",
1391
+ "Run the goal verification commands.",
1392
+ {
1393
+ stopOnFailure: {
1394
+ type: "boolean",
1395
+ description: "Reserved; checks currently stop on first failure.",
1396
+ default: true
1397
+ }
1398
+ }
1399
+ )
1400
+ },
1401
+ {
1402
+ family: "artifacts",
1403
+ canonicalName: "artifacts.create",
1404
+ aliases: ["artifacts", "artifacts_create", "artifacts.create"],
1405
+ schema: tool(
1406
+ "artifacts_create",
1407
+ "Create a goal artifact from text content.",
1408
+ {
1409
+ kind: {
1410
+ type: "string",
1411
+ enum: ["command_log", "check_log", "patch", "trace", "manifest", "result"]
1412
+ },
1413
+ name: stringSchema("Artifact file name."),
1414
+ content: stringSchema("Artifact text content."),
1415
+ mimeType: {
1416
+ ...stringSchema("Artifact MIME type."),
1417
+ default: "text/plain"
1418
+ }
1419
+ },
1420
+ ["kind", "name", "content"]
1421
+ )
1422
+ },
1423
+ {
1424
+ family: "source",
1425
+ canonicalName: "source.finalize",
1426
+ aliases: ["source", "source_finalize", "source.finalize"],
1427
+ schema: tool(
1428
+ "source_finalize",
1429
+ "Commit and push checked source changes when policy allows.",
1430
+ {
1431
+ checksPassed: {
1432
+ type: "boolean",
1433
+ description: "Whether required checks passed."
1434
+ },
1435
+ defaultBranch: {
1436
+ ...stringSchema("Default branch to push to, when known."),
1437
+ default: ""
1438
+ }
1439
+ },
1440
+ ["checksPassed"]
1441
+ )
1442
+ },
1443
+ ...OPENPOND_AGENT_SDK_COMMANDS.map((sdkCommand) => ({
1444
+ family: "openpond_agent_sdk",
1445
+ canonicalName: `openpond_agent.${sdkCommand}`,
1446
+ aliases: [`openpond_agent_${sdkCommand}`],
1447
+ schema: tool(
1448
+ `openpond_agent_${sdkCommand}`,
1449
+ `Run project-local openpond-agent ${sdkCommand}.`,
1450
+ {
1451
+ args: stringArraySchema("Additional CLI args."),
1452
+ json: {
1453
+ type: "boolean",
1454
+ description: "Request JSON output when supported.",
1455
+ default: sdkCommand === "inspect" || sdkCommand === "eval" || sdkCommand === "traces"
1456
+ }
1457
+ },
1458
+ []
1459
+ )
1460
+ })),
1461
+ {
1462
+ family: "openpond_agent_sdk",
1463
+ canonicalName: "openpond_agent.run",
1464
+ aliases: ["openpond_agent_run"],
1465
+ schema: tool(
1466
+ "openpond_agent_run",
1467
+ "Run a project-local openpond-agent action.",
1468
+ {
1469
+ args: stringArraySchema("CLI args, such as --action and --input."),
1470
+ json: {
1471
+ type: "boolean",
1472
+ description: "Request JSON output when supported.",
1473
+ default: true
1474
+ }
1475
+ },
1476
+ []
1477
+ )
1478
+ },
1479
+ {
1480
+ family: "openpond_agent_sdk",
1481
+ canonicalName: "openpond_agent.default_checks",
1482
+ aliases: [
1483
+ "openpond_agent_default_checks",
1484
+ "openpond_agent.default_checks"
1485
+ ],
1486
+ schema: tool(
1487
+ "openpond_agent_default_checks",
1488
+ "Run inspect, build, validate, and eval through the project-local SDK.",
1489
+ {}
1490
+ )
1491
+ }
1492
+ ];
1493
+ var ALIAS_TO_CANONICAL = new Map(
1494
+ GOAL_TOOL_REGISTRY.flatMap((registration) => [
1495
+ [registration.canonicalName, registration.canonicalName],
1496
+ ...registration.aliases.map(
1497
+ (alias) => [alias, registration.canonicalName]
1498
+ )
1499
+ ])
1500
+ );
1501
+ function buildGoalLlmToolSchemas(toolNames) {
1502
+ const enabled = new Set(toolNames);
1503
+ return GOAL_TOOL_REGISTRY.flatMap(
1504
+ (registration) => enabled.has(registration.family) && registration.schema ? [registration.schema] : []
1505
+ );
1506
+ }
1507
+ function normalizeGoalToolName(name, args) {
1508
+ const raw = name.trim();
1509
+ const canonical = ALIAS_TO_CANONICAL.get(raw);
1510
+ if (canonical) return canonical;
1511
+ const sdkPrefix = "openpond_agent_";
1512
+ if (raw.startsWith(sdkPrefix)) {
1513
+ return `openpond_agent.${raw.slice(sdkPrefix.length)}`;
1514
+ }
1515
+ if (raw === "files") {
1516
+ const action = optionalString(args, "action");
1517
+ return action ? `files.${action}` : raw;
1518
+ }
1519
+ if (raw === "openpond_agent_sdk") {
1520
+ const command = optionalString(args, "command");
1521
+ return command ? `openpond_agent.${command}` : raw;
1522
+ }
1523
+ return raw;
1524
+ }
1525
+ function tool(name, description, properties, required = []) {
1526
+ return {
1527
+ type: "function",
1528
+ function: {
1529
+ name,
1530
+ description,
1531
+ parameters: {
1532
+ type: "object",
1533
+ properties,
1534
+ required,
1535
+ additionalProperties: false
1536
+ }
1537
+ }
1538
+ };
1539
+ }
1540
+ function stringSchema(description) {
1541
+ return { type: "string", description };
1542
+ }
1543
+ function stringArraySchema(description) {
1544
+ return {
1545
+ type: "array",
1546
+ description,
1547
+ items: { type: "string" },
1548
+ default: []
1549
+ };
1550
+ }
1551
+ function optionalString(args, key) {
1552
+ const value = args[key];
1553
+ return typeof value === "string" && value.trim() ? value.trim() : null;
1554
+ }
1555
+
1556
+ // src/goal/tools/dispatch.ts
1557
+ import { Buffer as Buffer3 } from "node:buffer";
1558
+ import { randomUUID as randomUUID7 } from "node:crypto";
1559
+ import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve3 } from "node:path";
1560
+
1561
+ // src/goal/approvals.ts
1562
+ function createGoalApprovalRequest(input) {
1563
+ return {
1564
+ goalId: input.goal.id,
1565
+ kind: input.kind,
1566
+ title: input.title,
1567
+ reason: input.reason,
1568
+ payload: input.payload ?? {},
1569
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1570
+ };
1571
+ }
1572
+
1573
+ // src/goal/tools/files.ts
1574
+ import { constants as fsConstants } from "node:fs";
1575
+ import { access, mkdir as mkdir2, readFile, readdir, stat, writeFile as writeFile2 } from "node:fs/promises";
1576
+ import { dirname, isAbsolute, relative, resolve as resolve2 } from "node:path";
1577
+ function resolveWorkspacePath(workspace, filePath) {
1578
+ const resolved = resolve2(workspace, filePath);
1579
+ const rel = relative(workspace, resolved);
1580
+ if (rel.startsWith("..") || isAbsolute(rel)) {
1581
+ throw new Error(`path is outside workspace: ${filePath}`);
1582
+ }
1583
+ return resolved;
1584
+ }
1585
+ async function readGoalPath(params) {
1586
+ const absolute = resolveWorkspacePath(params.workspace, params.path);
1587
+ const info = await stat(absolute);
1588
+ if (info.isDirectory()) {
1589
+ const entries = await readdir(absolute, { withFileTypes: true });
1590
+ return {
1591
+ path: params.path,
1592
+ type: "directory",
1593
+ entries: entries.map((entry) => entry.name).sort()
1594
+ };
1595
+ }
1596
+ assertGoalFileReadSize(params.path, info.size);
1597
+ return {
1598
+ path: params.path,
1599
+ type: "file",
1600
+ content: await readFile(absolute, "utf-8")
1601
+ };
1602
+ }
1603
+ async function writeGoalFile(params) {
1604
+ const absolute = resolveWorkspacePath(params.workspace, params.path);
1605
+ const created = !await fileExists(absolute);
1606
+ await mkdir2(dirname(absolute), { recursive: true });
1607
+ await writeFile2(absolute, params.content, "utf-8");
1608
+ return { path: params.path, created };
1609
+ }
1610
+ async function listGoalFiles(params) {
1611
+ const root = resolveWorkspacePath(params.workspace, params.path || ".");
1612
+ const entries = await readdir(root, { withFileTypes: true });
1613
+ return entries.map((entry) => entry.name).sort();
1614
+ }
1615
+ async function fileExists(path) {
1616
+ return access(path, fsConstants.F_OK).then(() => true).catch(() => false);
1617
+ }
1618
+
1619
+ // src/goal/tools/questions.ts
1620
+ import { randomUUID as randomUUID6 } from "node:crypto";
1621
+ async function askGoalQuestion(params) {
1622
+ const question = {
1623
+ id: `question_${randomUUID6()}`,
1624
+ goalId: params.goalId,
1625
+ title: params.title,
1626
+ reason: params.reason,
1627
+ required: params.required ?? true,
1628
+ options: params.options ?? [],
1629
+ freeformAllowed: params.freeformAllowed ?? true,
1630
+ answeredAt: null
1631
+ };
1632
+ await params.localState?.addQuestion(params.goalId, question);
1633
+ await params.hostedClient?.createQuestion(params.goalId, question);
1634
+ await recordGoalEvent(
1635
+ createGoalEvent({
1636
+ goalId: params.goalId,
1637
+ iterationId: params.iterationId,
1638
+ kind: "question.created",
1639
+ summary: `Question created: ${question.title}`,
1640
+ payload: {
1641
+ questionId: question.id,
1642
+ required: question.required,
1643
+ options: question.options,
1644
+ freeformAllowed: question.freeformAllowed
1645
+ }
1646
+ }),
1647
+ { localState: params.localState, hostedClient: params.hostedClient }
1648
+ );
1649
+ return question;
1650
+ }
1651
+
1652
+ // src/goal/tools/sdk-agent.ts
1653
+ import { existsSync } from "node:fs";
1654
+ import { createRequire } from "node:module";
1655
+ import { join as join3 } from "node:path";
1656
+ function resolveProjectLocalAgentCommand(cwd) {
1657
+ const packageDistCli = join3(
1658
+ cwd,
1659
+ "node_modules",
1660
+ "openpond-agent-sdk",
1661
+ "dist",
1662
+ "cli.js"
1663
+ );
1664
+ if (existsSync(packageDistCli)) return { command: process.execPath, args: [packageDistCli] };
1665
+ const localBin = join3(cwd, "node_modules", ".bin", "openpond-agent");
1666
+ if (existsSync(localBin)) return { command: localBin, args: [] };
1667
+ const packageCli = join3(
1668
+ cwd,
1669
+ "node_modules",
1670
+ "openpond-agent-sdk",
1671
+ "src",
1672
+ "cli.ts"
1673
+ );
1674
+ if (existsSync(packageCli)) {
1675
+ const packageJson = join3(cwd, "node_modules", "openpond-agent-sdk", "package.json");
1676
+ const tsxCli = createRequire(packageJson).resolve("tsx/cli");
1677
+ return { command: process.execPath, args: [tsxCli, packageCli] };
1678
+ }
1679
+ return null;
1680
+ }
1681
+ async function runAgentSdkCommand(params) {
1682
+ if (params.sdkCommand === "inspect" && !resolveProjectLocalAgentCommand(params.cwd) && !existsSync(join3(params.cwd, "package.json"))) {
1683
+ return uninitializedAgentInspectResult(params.cwd);
1684
+ }
1685
+ const command = await ensureProjectLocalAgentCommand(params);
1686
+ const args = [
1687
+ ...command.args,
1688
+ params.sdkCommand,
1689
+ ...params.args ?? [],
1690
+ "--cwd",
1691
+ params.cwd
1692
+ ];
1693
+ if (params.json) args.push("--json");
1694
+ return runGoalShellCommand({
1695
+ goalId: params.goalId,
1696
+ iterationId: params.iterationId,
1697
+ command: [command.command, ...args].map(shellEscape).join(" "),
1698
+ cwd: params.cwd,
1699
+ workspace: params.workspace,
1700
+ storageRoot: params.storageRoot,
1701
+ localState: params.localState,
1702
+ hostedClient: params.hostedClient
1703
+ });
1704
+ }
1705
+ function uninitializedAgentInspectResult(cwd) {
1706
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1707
+ return {
1708
+ command: "openpond-agent inspect --json",
1709
+ cwd,
1710
+ code: 0,
1711
+ timedOut: false,
1712
+ durationMs: 0,
1713
+ stdoutTail: JSON.stringify({
1714
+ status: "not_initialized",
1715
+ summary: "No OpenPond agent project was found yet. Create package.json and source files before running SDK validation.",
1716
+ packageJsonFound: false,
1717
+ projectLocalOpenpondAgentFound: false
1718
+ }),
1719
+ stderrTail: "",
1720
+ artifactRefs: [],
1721
+ startedAt: now,
1722
+ completedAt: now
1723
+ };
1724
+ }
1725
+ async function runDefaultAgentSdkChecks(params) {
1726
+ const commands = [
1727
+ "inspect",
1728
+ "build",
1729
+ "validate",
1730
+ "eval",
1731
+ "traces"
1732
+ ];
1733
+ const results = [];
1734
+ for (const sdkCommand of commands) {
1735
+ const result = await runAgentSdkCommand({
1736
+ ...params,
1737
+ sdkCommand,
1738
+ json: sdkCommand === "inspect" || sdkCommand === "eval" || sdkCommand === "traces"
1739
+ });
1740
+ results.push(result);
1741
+ if (result.code !== 0 || result.timedOut) break;
1742
+ }
1743
+ return results;
1744
+ }
1745
+ async function ensureProjectLocalAgentCommand(params) {
1746
+ const existing = resolveProjectLocalAgentCommand(params.cwd);
1747
+ if (existing) return existing;
1748
+ const installCommand = dependencyInstallCommand(params.cwd);
1749
+ if (!installCommand) {
1750
+ throw new Error(
1751
+ "project-local openpond-agent is missing and no package.json was found"
1752
+ );
1753
+ }
1754
+ const setup = await runGoalShellCommand({
1755
+ goalId: params.goalId,
1756
+ iterationId: params.iterationId,
1757
+ command: installCommand,
1758
+ cwd: params.cwd,
1759
+ timeoutSeconds: 600,
1760
+ workspace: params.workspace,
1761
+ storageRoot: params.storageRoot,
1762
+ localState: params.localState,
1763
+ hostedClient: params.hostedClient
1764
+ });
1765
+ if (setup.code !== 0 || setup.timedOut) {
1766
+ throw new Error(`dependency setup failed before openpond-agent: ${installCommand}`);
1767
+ }
1768
+ const installed = resolveProjectLocalAgentCommand(params.cwd);
1769
+ if (!installed) {
1770
+ throw new Error("project-local openpond-agent was not created by dependency setup");
1771
+ }
1772
+ return installed;
1773
+ }
1774
+ function dependencyInstallCommand(cwd) {
1775
+ if (!existsSync(join3(cwd, "package.json"))) return null;
1776
+ if (existsSync(join3(cwd, "pnpm-lock.yaml"))) return "pnpm install";
1777
+ if (existsSync(join3(cwd, "yarn.lock"))) return "yarn install";
1778
+ if (existsSync(join3(cwd, "package-lock.json"))) return "npm install";
1779
+ return "npm install";
1780
+ }
1781
+ function shellEscape(value) {
1782
+ if (/^[A-Za-z0-9_./:=@+-]+$/.test(value)) return value;
1783
+ return `'${value.replace(/'/g, "'\\''")}'`;
1784
+ }
1785
+
1786
+ // src/goal/tools/source.ts
1787
+ async function finalizeCheckedSourceUpdate(params) {
1788
+ if (params.goal.executionPolicy.sourceUpdates !== "auto_commit_push_default_branch") {
1789
+ return blocked("source update policy is propose-only");
1790
+ }
1791
+ if (!params.checksPassed) return blocked("required checks have not passed");
1792
+ if (params.hostedClient) {
1793
+ return {
1794
+ status: "skipped",
1795
+ branch: null,
1796
+ commitMessage: null,
1797
+ summary: "Source finalization delegated to OpenPond control plane"
1798
+ };
1799
+ }
1800
+ const status = await runGoalShellCommand({
1801
+ goalId: params.goal.id,
1802
+ iterationId: params.iterationId,
1803
+ command: "git status --porcelain -uall",
1804
+ cwd: params.cwd,
1805
+ workspace: params.workspace,
1806
+ storageRoot: params.storageRoot,
1807
+ localState: params.localState,
1808
+ hostedClient: params.hostedClient
1809
+ });
1810
+ if (!status.stdoutTail.trim()) {
1811
+ return {
1812
+ status: "skipped",
1813
+ branch: null,
1814
+ commitMessage: null,
1815
+ summary: "No source changes to commit"
1816
+ };
1817
+ }
1818
+ const unsafePath = unsafeSourceFinalizationPathFromStatus(status.stdoutTail);
1819
+ if (unsafePath) {
1820
+ return blocked(`source update includes disallowed path: ${unsafePath}`);
1821
+ }
1822
+ const branch = params.defaultBranch?.trim() || await resolveDefaultBranch({
1823
+ ...params
1824
+ });
1825
+ if (!branch) return blocked("could not resolve project default branch");
1826
+ const message = [
1827
+ `OpenPond Goal: ${params.goal.objective.slice(0, 72)}`,
1828
+ "",
1829
+ `Goal: ${params.goal.id}`,
1830
+ `Iteration: ${params.iterationId}`
1831
+ ].join("\n");
1832
+ const add = await runGoalShellCommand({
1833
+ goalId: params.goal.id,
1834
+ iterationId: params.iterationId,
1835
+ command: "git add -A",
1836
+ cwd: params.cwd,
1837
+ workspace: params.workspace,
1838
+ storageRoot: params.storageRoot,
1839
+ localState: params.localState,
1840
+ hostedClient: params.hostedClient
1841
+ });
1842
+ if (add.code !== 0) return blocked("git add failed");
1843
+ const commit = await runGoalShellCommand({
1844
+ goalId: params.goal.id,
1845
+ iterationId: params.iterationId,
1846
+ command: `git -c user.name='OpenPond Goal' -c user.email=goals@openpond.ai commit -m ${shellEscape2(message)}`,
1847
+ cwd: params.cwd,
1848
+ workspace: params.workspace,
1849
+ storageRoot: params.storageRoot,
1850
+ localState: params.localState,
1851
+ hostedClient: params.hostedClient
1852
+ });
1853
+ if (commit.code !== 0) return blocked("git commit failed");
1854
+ const push = await runGoalShellCommand({
1855
+ goalId: params.goal.id,
1856
+ iterationId: params.iterationId,
1857
+ command: `git push origin HEAD:${shellEscape2(branch)}`,
1858
+ cwd: params.cwd,
1859
+ workspace: params.workspace,
1860
+ storageRoot: params.storageRoot,
1861
+ localState: params.localState,
1862
+ hostedClient: params.hostedClient
1863
+ });
1864
+ if (push.code !== 0) return blocked("git push failed");
1865
+ const result = {
1866
+ status: "committed",
1867
+ branch,
1868
+ commitMessage: message,
1869
+ summary: `Committed and pushed checked source update to ${branch}`
1870
+ };
1871
+ await recordGoalEvent(
1872
+ createGoalEvent({
1873
+ goalId: params.goal.id,
1874
+ iterationId: params.iterationId,
1875
+ kind: "source.updated",
1876
+ summary: result.summary,
1877
+ payload: {
1878
+ branch,
1879
+ policy: params.goal.executionPolicy.sourceUpdates
1880
+ }
1881
+ }),
1882
+ { localState: params.localState, hostedClient: params.hostedClient }
1883
+ );
1884
+ return result;
1885
+ function blocked(summary) {
1886
+ return {
1887
+ status: "blocked",
1888
+ branch: null,
1889
+ commitMessage: null,
1890
+ summary
1891
+ };
1892
+ }
1893
+ }
1894
+ function unsafeSourceFinalizationPathFromStatus(statusOutput) {
1895
+ if (statusOutput.includes("[truncated ")) return "git status output was truncated";
1896
+ for (const line of statusOutput.split(/\r?\n/)) {
1897
+ const paths = porcelainStatusPaths(line);
1898
+ const unsafe = paths.find(isUnsafeSourceFinalizationPath);
1899
+ if (unsafe) return unsafe;
1900
+ }
1901
+ return null;
1902
+ }
1903
+ function porcelainStatusPaths(line) {
1904
+ if (line.length < 4) return [];
1905
+ const raw = line.slice(3).trim();
1906
+ if (!raw) return [];
1907
+ return raw.split(" -> ").map((path) => path.replace(/^"|"$/g, "").trim()).filter(Boolean);
1908
+ }
1909
+ function isUnsafeSourceFinalizationPath(path) {
1910
+ return path === ".env" || path.startsWith(".env.") || path.includes("/.env") || path === "node_modules" || path.startsWith("node_modules/") || path.includes("/node_modules/") || path === ".git" || path.startsWith(".git/") || path.includes("/.git/");
1911
+ }
1912
+ async function resolveDefaultBranch(params) {
1913
+ const remoteHead = await runGoalShellCommand({
1914
+ goalId: params.goal.id,
1915
+ iterationId: params.iterationId,
1916
+ command: "git symbolic-ref --short refs/remotes/origin/HEAD",
1917
+ cwd: params.cwd,
1918
+ workspace: params.workspace,
1919
+ storageRoot: params.storageRoot,
1920
+ localState: params.localState,
1921
+ hostedClient: params.hostedClient
1922
+ });
1923
+ const branch = remoteHead.stdoutTail.trim().replace(/^origin\//, "");
1924
+ if (remoteHead.code === 0 && branch) return branch;
1925
+ const current = await runGoalShellCommand({
1926
+ goalId: params.goal.id,
1927
+ iterationId: params.iterationId,
1928
+ command: "git branch --show-current",
1929
+ cwd: params.cwd,
1930
+ workspace: params.workspace,
1931
+ storageRoot: params.storageRoot,
1932
+ localState: params.localState,
1933
+ hostedClient: params.hostedClient
1934
+ });
1935
+ const currentBranch = current.stdoutTail.trim();
1936
+ return current.code === 0 && currentBranch ? currentBranch : null;
1937
+ }
1938
+ function shellEscape2(value) {
1939
+ if (/^[A-Za-z0-9_./:=@+-]+$/.test(value)) return value;
1940
+ return `'${value.replace(/'/g, "'\\''")}'`;
1941
+ }
1942
+
1943
+ // src/goal/tools/dispatch.ts
1944
+ async function runGoalLlmToolCall(context, toolCall) {
1945
+ const normalizedName = normalizeGoalToolName(toolCall.name, toolCall.arguments);
1946
+ try {
1947
+ return await runKnownTool(context, toolCall, normalizedName);
1948
+ } catch (error) {
1949
+ const summary = error instanceof Error ? error.message : `Tool failed: ${normalizedName}`;
1950
+ await recordGoalEvent(
1951
+ createGoalEvent({
1952
+ goalId: context.goal.id,
1953
+ iterationId: context.iterationId,
1954
+ kind: "goal.blocked",
1955
+ summary,
1956
+ payload: {
1957
+ toolCallId: toolCall.id,
1958
+ toolName: normalizedName
1959
+ }
1960
+ }),
1961
+ context
1962
+ );
1963
+ return {
1964
+ toolCallId: toolCall.id,
1965
+ name: normalizedName,
1966
+ status: "blocked",
1967
+ summary
1968
+ };
1969
+ }
1970
+ }
1971
+ async function runKnownTool(context, toolCall, normalizedName) {
1972
+ const args = toolCall.arguments;
1973
+ if (normalizedName === "files.read") {
1974
+ const path = requireString(args, "path");
1975
+ const result = await readGoalPath({ workspace: context.workspace, path });
1976
+ if (result.type === "directory") {
1977
+ return ok(toolCall, normalizedName, `Path is a directory; listed entries: ${path}`, {
1978
+ path,
1979
+ type: "directory",
1980
+ entries: result.entries,
1981
+ next: "Use files.read with one of the listed file paths, or files.list for another directory."
1982
+ });
1983
+ }
1984
+ const content = result.content;
1985
+ return ok(toolCall, normalizedName, `Read file: ${path}`, {
1986
+ path,
1987
+ type: "file",
1988
+ contentTail: truncateForEvent(content)
1989
+ });
1990
+ }
1991
+ if (normalizedName === "files.write") {
1992
+ const path = requireString(args, "path");
1993
+ const content = requireRawString(args, "content");
1994
+ const policyBlock = hostedFileWritePolicyBlock(context, path);
1995
+ if (policyBlock) {
1996
+ await recordGoalEvent(
1997
+ createGoalEvent({
1998
+ goalId: context.goal.id,
1999
+ iterationId: context.iterationId,
2000
+ kind: "command.completed",
2001
+ summary: policyBlock.summary,
2002
+ payload: {
2003
+ path: policyBlock.path,
2004
+ reason: policyBlock.reason,
2005
+ skipped: true,
2006
+ next: policyBlock.next
2007
+ }
2008
+ }),
2009
+ context
2010
+ );
2011
+ return ok(toolCall, normalizedName, policyBlock.summary, {
2012
+ path: policyBlock.path,
2013
+ reason: policyBlock.reason,
2014
+ skipped: true,
2015
+ next: policyBlock.next
2016
+ });
2017
+ }
2018
+ const result = await writeGoalFile({ workspace: context.workspace, path, content });
2019
+ await recordGoalEvent(
2020
+ createGoalEvent({
2021
+ goalId: context.goal.id,
2022
+ iterationId: context.iterationId,
2023
+ kind: "source.updated",
2024
+ summary: `${result.created ? "Created" : "Updated"} file: ${path}`,
2025
+ payload: {
2026
+ path,
2027
+ created: result.created,
2028
+ bytes: Buffer3.byteLength(content, "utf-8")
2029
+ }
2030
+ }),
2031
+ context
2032
+ );
2033
+ return ok(toolCall, normalizedName, `${result.created ? "Created" : "Updated"} file: ${path}`, result);
2034
+ }
2035
+ if (normalizedName === "files.list") {
2036
+ const path = optionalString2(args, "path") || ".";
2037
+ const entries = await listGoalFiles({ workspace: context.workspace, path });
2038
+ return ok(toolCall, normalizedName, `Listed files: ${path}`, {
2039
+ path,
2040
+ entries
2041
+ });
2042
+ }
2043
+ if (normalizedName === "shell.run") {
2044
+ const command = requireString(args, "command");
2045
+ const policyBlock = hostedShellCommandPolicyBlock(context, command);
2046
+ if (policyBlock) {
2047
+ await recordGoalEvent(
2048
+ createGoalEvent({
2049
+ goalId: context.goal.id,
2050
+ iterationId: context.iterationId,
2051
+ kind: "command.completed",
2052
+ summary: policyBlock.summary,
2053
+ payload: {
2054
+ command: policyBlock.command,
2055
+ reason: policyBlock.reason,
2056
+ skipped: true,
2057
+ next: policyBlock.next
2058
+ }
2059
+ }),
2060
+ context
2061
+ );
2062
+ return {
2063
+ toolCallId: toolCall.id,
2064
+ name: normalizedName,
2065
+ status: "ok",
2066
+ summary: policyBlock.summary,
2067
+ output: {
2068
+ reason: policyBlock.reason,
2069
+ skipped: true,
2070
+ next: policyBlock.next
2071
+ }
2072
+ };
2073
+ }
2074
+ const cwd = resolveToolCwd(context.workspace, optionalString2(args, "cwd"));
2075
+ const result = await runGoalShellCommand({
2076
+ goalId: context.goal.id,
2077
+ iterationId: context.iterationId,
2078
+ command,
2079
+ cwd,
2080
+ timeoutSeconds: optionalNumber(args, "timeoutSeconds") ?? void 0,
2081
+ workspace: context.workspace,
2082
+ storageRoot: context.storageRoot,
2083
+ localState: context.localState,
2084
+ hostedClient: context.hostedClient
2085
+ });
2086
+ const passed = result.code === 0 && !result.timedOut;
2087
+ return {
2088
+ toolCallId: toolCall.id,
2089
+ name: normalizedName,
2090
+ status: passed ? "ok" : "blocked",
2091
+ summary: passed ? `Command completed: ${command}` : `Command failed: ${command}`,
2092
+ output: {
2093
+ code: result.code,
2094
+ timedOut: result.timedOut,
2095
+ stdoutTail: result.stdoutTail,
2096
+ stderrTail: result.stderrTail
2097
+ }
2098
+ };
2099
+ }
2100
+ if (normalizedName === "questions.ask") {
2101
+ const question = await askGoalQuestion({
2102
+ goalId: context.goal.id,
2103
+ iterationId: context.iterationId,
2104
+ title: requireString(args, "title"),
2105
+ reason: requireString(args, "reason"),
2106
+ required: optionalBoolean(args, "required") ?? true,
2107
+ freeformAllowed: optionalBoolean(args, "freeformAllowed") ?? true,
2108
+ options: normalizeQuestionOptions(args.options),
2109
+ localState: context.localState,
2110
+ hostedClient: context.hostedClient
2111
+ });
2112
+ return {
2113
+ toolCallId: toolCall.id,
2114
+ name: normalizedName,
2115
+ status: question.required ? "needs_user_input" : "ok",
2116
+ summary: `Question created: ${question.title}`,
2117
+ output: { questionId: question.id, required: question.required }
2118
+ };
2119
+ }
2120
+ if (normalizedName === "approvals.request") {
2121
+ const kind = normalizeApprovalKind(requireString(args, "kind"));
2122
+ const request = createGoalApprovalRequest({
2123
+ goal: context.goal,
2124
+ kind,
2125
+ title: requireString(args, "title"),
2126
+ reason: requireString(args, "reason"),
2127
+ payload: optionalRecord(args, "payload")
2128
+ });
2129
+ if (context.hostedClient) {
2130
+ await context.hostedClient.requestApproval(request);
2131
+ } else {
2132
+ await recordGoalEvent(
2133
+ createGoalEvent({
2134
+ goalId: context.goal.id,
2135
+ iterationId: context.iterationId,
2136
+ kind: "approval.requested",
2137
+ summary: request.title,
2138
+ payload: {
2139
+ ...request.payload,
2140
+ kind: request.kind,
2141
+ reason: request.reason
2142
+ }
2143
+ }),
2144
+ context
2145
+ );
2146
+ if (context.localState) {
2147
+ const current = await context.localState.get(context.goal.id);
2148
+ await context.localState.update({
2149
+ ...current ?? context.goal,
2150
+ status: "awaiting_approval",
2151
+ approvals: [
2152
+ ...(current ?? context.goal).approvals ?? [],
2153
+ {
2154
+ ...request,
2155
+ id: `approval_${randomUUID7()}`,
2156
+ status: "pending",
2157
+ decidedAt: null,
2158
+ decisionNote: null
2159
+ }
2160
+ ],
2161
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2162
+ });
2163
+ }
2164
+ }
2165
+ return {
2166
+ toolCallId: toolCall.id,
2167
+ name: normalizedName,
2168
+ status: "needs_approval",
2169
+ summary: `Approval requested: ${request.title}`,
2170
+ output: {
2171
+ kind: request.kind
2172
+ }
2173
+ };
2174
+ }
2175
+ if (normalizedName === "checks.run") {
2176
+ const checks = await runGoalVerificationChecks({
2177
+ goal: context.goal,
2178
+ iterationId: context.iterationId,
2179
+ cwd: context.workspace,
2180
+ workspace: context.workspace,
2181
+ storageRoot: context.storageRoot,
2182
+ localState: context.localState,
2183
+ hostedClient: context.hostedClient
2184
+ });
2185
+ const failed = checks.find((check) => check.status !== "passed");
2186
+ return {
2187
+ toolCallId: toolCall.id,
2188
+ name: normalizedName,
2189
+ status: failed ? "blocked" : "ok",
2190
+ summary: failed ? "Goal checks failed" : "Goal checks passed",
2191
+ output: { checks: compactChecks(checks) },
2192
+ checksPassed: !failed
2193
+ };
2194
+ }
2195
+ if (normalizedName === "artifacts.create") {
2196
+ const artifact = createGoalArtifact({
2197
+ goalId: context.goal.id,
2198
+ iterationId: context.iterationId,
2199
+ kind: normalizeArtifactKind(optionalString2(args, "kind")),
2200
+ name: requireString(args, "name"),
2201
+ mimeType: optionalString2(args, "mimeType") || "text/plain",
2202
+ content: requireRawString(args, "content")
2203
+ });
2204
+ const ref = await recordGoalArtifact({
2205
+ artifact,
2206
+ workspace: context.workspace,
2207
+ storageRoot: context.storageRoot,
2208
+ localState: context.localState,
2209
+ hostedClient: context.hostedClient
2210
+ });
2211
+ return ok(toolCall, normalizedName, `Artifact created: ${artifact.name}`, {
2212
+ artifactRef: ref.ref,
2213
+ artifactId: ref.id,
2214
+ bytes: ref.bytes
2215
+ });
2216
+ }
2217
+ if (normalizedName === "source.finalize") {
2218
+ const result = await finalizeCheckedSourceUpdate({
2219
+ goal: context.goal,
2220
+ iterationId: context.iterationId,
2221
+ cwd: context.workspace,
2222
+ checksPassed: optionalBoolean(args, "checksPassed") ?? false,
2223
+ defaultBranch: optionalString2(args, "defaultBranch") || null,
2224
+ workspace: context.workspace,
2225
+ storageRoot: context.storageRoot,
2226
+ localState: context.localState,
2227
+ hostedClient: context.hostedClient
2228
+ });
2229
+ return {
2230
+ toolCallId: toolCall.id,
2231
+ name: normalizedName,
2232
+ status: result.status === "blocked" ? "blocked" : "ok",
2233
+ summary: result.summary,
2234
+ output: result
2235
+ };
2236
+ }
2237
+ if (normalizedName.startsWith("openpond_agent.")) {
2238
+ return runAgentTool(context, toolCall, normalizedName);
2239
+ }
2240
+ throw new Error(`Unsupported goal tool: ${toolCall.name}`);
2241
+ }
2242
+ function hostedFileWritePolicyBlock(context, filePath) {
2243
+ if (!context.hostedClient) return null;
2244
+ const path = normalizedWorkspaceRelativePath(context.workspace, filePath);
2245
+ if (!path) return null;
2246
+ if (isHostedProtectedWritePath(path)) {
2247
+ return {
2248
+ path,
2249
+ reason: "hosted_protected_source_path",
2250
+ summary: `Skipped write to protected hosted path: ${path}`,
2251
+ next: "Use normal source files for edits. Do not write generated metadata, dependency directories, git internals, or secret/env files."
2252
+ };
2253
+ }
2254
+ if (context.goal.profile === "openpond_agent" && path === "openpond.yaml") {
2255
+ return {
2256
+ path,
2257
+ reason: "hosted_openpond_agent_manifest_write_disallowed",
2258
+ summary: "Skipped write to generated OpenPond agent manifest: openpond.yaml",
2259
+ next: "For hosted OpenPond-agent goals, edit agent/** source files and run openpond_agent_* tools. Do not change openpond.yaml directly."
2260
+ };
2261
+ }
2262
+ return null;
2263
+ }
2264
+ function normalizedWorkspaceRelativePath(workspace, filePath) {
2265
+ const resolved = resolve3(workspace, filePath);
2266
+ const rel = relative2(workspace, resolved);
2267
+ if (!rel || rel.startsWith("..") || isAbsolute2(rel)) return null;
2268
+ return rel.replace(/\\/g, "/");
2269
+ }
2270
+ function isHostedProtectedWritePath(path) {
2271
+ return path === ".env" || path.startsWith(".env.") || path.includes("/.env") || path === ".git" || path.startsWith(".git/") || path === ".openpond" || path.startsWith(".openpond/") || path === "node_modules" || path.startsWith("node_modules/");
2272
+ }
2273
+ function hostedShellCommandPolicyBlock(context, command) {
2274
+ if (!context.hostedClient || context.goal.profile !== "openpond_agent") {
2275
+ return null;
2276
+ }
2277
+ if (!/\bopenpond-agent\b/.test(command)) return null;
2278
+ return {
2279
+ command,
2280
+ reason: "hosted_openpond_agent_shell_disallowed",
2281
+ summary: "Skipped direct OpenPond agent shell command; use openpond_agent_* tools",
2282
+ next: "Call openpond_agent_inspect, openpond_agent_build, openpond_agent_validate, openpond_agent_eval, openpond_agent_traces, openpond_agent_run, or openpond_agent_default_checks."
2283
+ };
2284
+ }
2285
+ async function runAgentTool(context, toolCall, normalizedName) {
2286
+ if (normalizedName === "openpond_agent.default_checks") {
2287
+ const results = await runDefaultAgentSdkChecks({
2288
+ goalId: context.goal.id,
2289
+ iterationId: context.iterationId,
2290
+ cwd: context.workspace,
2291
+ workspace: context.workspace,
2292
+ storageRoot: context.storageRoot,
2293
+ localState: context.localState,
2294
+ hostedClient: context.hostedClient
2295
+ });
2296
+ const failed = results.find((result2) => result2.code !== 0 || result2.timedOut);
2297
+ return {
2298
+ toolCallId: toolCall.id,
2299
+ name: normalizedName,
2300
+ status: failed ? "blocked" : "ok",
2301
+ summary: failed ? "OpenPond agent SDK checks failed" : "OpenPond agent SDK checks passed",
2302
+ output: {
2303
+ commands: results.map((result2) => ({
2304
+ command: result2.command,
2305
+ code: result2.code,
2306
+ timedOut: result2.timedOut
2307
+ }))
2308
+ },
2309
+ checksPassed: !failed
2310
+ };
2311
+ }
2312
+ const sdkCommand = normalizedName.replace("openpond_agent.", "");
2313
+ if (!isAgentSdkCommand(sdkCommand)) {
2314
+ throw new Error(`Unsupported OpenPond agent SDK command: ${sdkCommand}`);
2315
+ }
2316
+ const result = await runAgentSdkCommand({
2317
+ goalId: context.goal.id,
2318
+ iterationId: context.iterationId,
2319
+ cwd: context.workspace,
2320
+ sdkCommand,
2321
+ args: optionalStringArray(toolCall.arguments, "args"),
2322
+ json: optionalBoolean(toolCall.arguments, "json") ?? shouldDefaultJson(sdkCommand),
2323
+ workspace: context.workspace,
2324
+ storageRoot: context.storageRoot,
2325
+ localState: context.localState,
2326
+ hostedClient: context.hostedClient
2327
+ });
2328
+ const passed = result.code === 0 && !result.timedOut;
2329
+ return {
2330
+ toolCallId: toolCall.id,
2331
+ name: normalizedName,
2332
+ status: passed ? "ok" : "blocked",
2333
+ summary: passed ? `openpond-agent ${sdkCommand} completed` : `openpond-agent ${sdkCommand} failed`,
2334
+ output: {
2335
+ code: result.code,
2336
+ timedOut: result.timedOut,
2337
+ stdoutTail: result.stdoutTail,
2338
+ stderrTail: result.stderrTail
2339
+ }
2340
+ };
2341
+ }
2342
+ function ok(toolCall, name, summary, output) {
2343
+ return {
2344
+ toolCallId: toolCall.id,
2345
+ name,
2346
+ status: "ok",
2347
+ summary,
2348
+ output
2349
+ };
2350
+ }
2351
+ function requireString(args, key) {
2352
+ const value = optionalString2(args, key);
2353
+ if (!value) throw new Error(`Tool argument ${key} must be a non-empty string`);
2354
+ return value;
2355
+ }
2356
+ function requireRawString(args, key) {
2357
+ const value = args[key];
2358
+ if (typeof value !== "string") {
2359
+ throw new Error(`Tool argument ${key} must be a string`);
2360
+ }
2361
+ return value;
2362
+ }
2363
+ function optionalString2(args, key) {
2364
+ const value = args[key];
2365
+ return typeof value === "string" && value.trim() ? value.trim() : null;
2366
+ }
2367
+ function optionalNumber(args, key) {
2368
+ const value = args[key];
2369
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
2370
+ }
2371
+ function optionalBoolean(args, key) {
2372
+ const value = args[key];
2373
+ return typeof value === "boolean" ? value : null;
2374
+ }
2375
+ function optionalStringArray(args, key) {
2376
+ const value = args[key];
2377
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
2378
+ }
2379
+ function optionalRecord(args, key) {
2380
+ const value = args[key];
2381
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
2382
+ return value;
2383
+ }
2384
+ function normalizeQuestionOptions(value) {
2385
+ if (!Array.isArray(value)) return [];
2386
+ return value.flatMap((item) => {
2387
+ if (!item || typeof item !== "object") return [];
2388
+ const record = item;
2389
+ const id = optionalString2(record, "id");
2390
+ const label = optionalString2(record, "label");
2391
+ if (!id || !label) return [];
2392
+ const description = optionalString2(record, "description");
2393
+ return [{ id, label, ...description ? { description } : {} }];
2394
+ });
2395
+ }
2396
+ function normalizeApprovalKind(value) {
2397
+ const allowed = /* @__PURE__ */ new Set([
2398
+ "create_plan",
2399
+ "deploy_publish",
2400
+ "integration_write",
2401
+ "secret_or_env_change",
2402
+ "budget_escalation",
2403
+ "external_effect"
2404
+ ]);
2405
+ if (!allowed.has(value)) {
2406
+ throw new Error(`Unsupported approval kind: ${value}`);
2407
+ }
2408
+ return value;
2409
+ }
2410
+ function normalizeArtifactKind(value) {
2411
+ const allowed = /* @__PURE__ */ new Set([
2412
+ "command_log",
2413
+ "check_log",
2414
+ "patch",
2415
+ "trace",
2416
+ "manifest",
2417
+ "result"
2418
+ ]);
2419
+ return value && allowed.has(value) ? value : "result";
2420
+ }
2421
+ function compactChecks(checks) {
2422
+ return checks.map((check) => ({
2423
+ id: check.id,
2424
+ name: check.name,
2425
+ status: check.status,
2426
+ code: check.code,
2427
+ timedOut: check.timedOut
2428
+ }));
2429
+ }
2430
+ function resolveToolCwd(workspace, cwd) {
2431
+ if (!cwd || cwd === ".") return workspace;
2432
+ const resolved = resolve3(workspace, cwd);
2433
+ const rel = relative2(workspace, resolved);
2434
+ if (rel.startsWith("..") || isAbsolute2(rel)) {
2435
+ throw new Error(`Tool cwd is outside workspace: ${cwd}`);
2436
+ }
2437
+ return resolved;
2438
+ }
2439
+ function isAgentSdkCommand(value) {
2440
+ return ["inspect", "build", "validate", "eval", "traces", "run"].includes(value);
2441
+ }
2442
+ function shouldDefaultJson(command) {
2443
+ return command === "inspect" || command === "eval" || command === "traces" || command === "run";
2444
+ }
2445
+
2446
+ // src/goal/runner.ts
2447
+ var MAX_GOAL_TOOL_ROUNDS = 24;
2448
+ async function runGoalIteration(params) {
2449
+ assertGoalCanStartIteration(params.config.goal);
2450
+ if (goalIterationBudgetRemaining(params.config.goal) <= 0) {
2451
+ const limited = createGoalEvent({
2452
+ goalId: params.config.goal.id,
2453
+ kind: "goal.blocked",
2454
+ summary: "Goal iteration budget exhausted",
2455
+ payload: {
2456
+ reason: "iteration_budget_exhausted",
2457
+ maxIterations: params.config.goal.budget.maxIterations
2458
+ }
2459
+ });
2460
+ await recordGoalEvent(limited, params);
2461
+ await updateGoalStatus(params, "budget_limited");
2462
+ return finishGoalRunResult(
2463
+ params,
2464
+ createGoalRunResult({
2465
+ goal: params.config.goal,
2466
+ status: "budget_limited",
2467
+ summary: limited.summary,
2468
+ events: [limited]
2469
+ })
2470
+ );
2471
+ }
2472
+ const iterationId = params.config.iterationId || `iteration_${randomUUID8()}`;
2473
+ const started = createGoalEvent({
2474
+ goalId: params.config.goal.id,
2475
+ iterationId,
2476
+ kind: "iteration.started",
2477
+ summary: "Goal iteration started",
2478
+ payload: {
2479
+ mode: params.config.mode,
2480
+ profile: params.config.goal.profile,
2481
+ promptPack: params.config.goal.promptPack
2482
+ }
2483
+ });
2484
+ await updateGoalStatus(params, "running");
2485
+ await recordGoalEvent(started, params);
2486
+ const events = [started];
2487
+ const profile = getGoalProfileDescriptor(params.config.goal);
2488
+ const workspace = params.config.workspace ?? process.cwd();
2489
+ const storageRoot = params.config.storageRoot ?? workspace;
2490
+ if (shouldRunLocalCreatePipeline(params.config.goal, params.config.mode)) {
2491
+ const result = await runLocalCreatePipeline({
2492
+ goal: params.config.goal,
2493
+ iterationId,
2494
+ workspace,
2495
+ localState: params.localState,
2496
+ startedEvents: events
2497
+ });
2498
+ return finishGoalRunResult(params, result);
2499
+ }
2500
+ let llmResponse = null;
2501
+ let toolResults = [];
2502
+ const toolSchemas = buildGoalLlmToolSchemas(profile.toolNames);
2503
+ try {
2504
+ const planner = await runGoalPlannerLoop({
2505
+ ...params,
2506
+ iterationId,
2507
+ workspace,
2508
+ storageRoot,
2509
+ toolSchemas,
2510
+ profileMetadata: { profile: profile.id, tools: profile.toolNames }
2511
+ });
2512
+ llmResponse = planner.response;
2513
+ toolResults = planner.toolResults;
2514
+ } catch (error) {
2515
+ const blocked2 = createGoalEvent({
2516
+ goalId: params.config.goal.id,
2517
+ iterationId,
2518
+ kind: "goal.blocked",
2519
+ summary: params.config.mode === "hosted" ? "Hosted Goal LLM route is not available" : "Local Goal LLM route is not available",
2520
+ payload: {
2521
+ error: error instanceof Error ? error.message : "LLM route failed"
2522
+ }
2523
+ });
2524
+ await recordGoalEvent(blocked2, params);
2525
+ await updateGoalStatus(params, "blocked");
2526
+ return finishGoalRunResult(
2527
+ params,
2528
+ createGoalRunResult({
2529
+ goal: params.config.goal,
2530
+ status: "blocked",
2531
+ summary: blocked2.summary,
2532
+ events: [...events, blocked2]
2533
+ })
2534
+ );
2535
+ }
2536
+ if (llmResponse) {
2537
+ const pausedForQuestion = toolResults.find(
2538
+ (result) => result.status === "needs_user_input"
2539
+ );
2540
+ if (pausedForQuestion) {
2541
+ const paused = createGoalEvent({
2542
+ goalId: params.config.goal.id,
2543
+ iterationId,
2544
+ kind: "iteration.completed",
2545
+ summary: pausedForQuestion.summary,
2546
+ payload: {
2547
+ status: "awaiting_user_input",
2548
+ toolResults: compactToolResults(toolResults)
2549
+ }
2550
+ });
2551
+ await recordGoalEvent(paused, params);
2552
+ await updateGoalStatus(params, "awaiting_user_input");
2553
+ return finishGoalRunResult(
2554
+ params,
2555
+ createGoalRunResult({
2556
+ goal: params.config.goal,
2557
+ status: "awaiting_user_input",
2558
+ summary: paused.summary,
2559
+ events: [...events, paused]
2560
+ })
2561
+ );
2562
+ }
2563
+ const pausedForApproval = toolResults.find(
2564
+ (result) => result.status === "needs_approval"
2565
+ );
2566
+ if (pausedForApproval) {
2567
+ const paused = createGoalEvent({
2568
+ goalId: params.config.goal.id,
2569
+ iterationId,
2570
+ kind: "iteration.completed",
2571
+ summary: pausedForApproval.summary,
2572
+ payload: {
2573
+ status: "awaiting_approval",
2574
+ toolResults: compactToolResults(toolResults)
2575
+ }
2576
+ });
2577
+ await recordGoalEvent(paused, params);
2578
+ await updateGoalStatus(params, "awaiting_approval");
2579
+ return finishGoalRunResult(
2580
+ params,
2581
+ createGoalRunResult({
2582
+ goal: params.config.goal,
2583
+ status: "awaiting_approval",
2584
+ summary: paused.summary,
2585
+ events: [...events, paused]
2586
+ })
2587
+ );
2588
+ }
2589
+ const blockedTool = latestUnresolvedBlockedToolResult(toolResults);
2590
+ if (blockedTool) {
2591
+ const blocked2 = createGoalEvent({
2592
+ goalId: params.config.goal.id,
2593
+ iterationId,
2594
+ kind: "goal.blocked",
2595
+ summary: blockedTool.summary,
2596
+ payload: {
2597
+ toolResults: compactToolResults(toolResults)
2598
+ }
2599
+ });
2600
+ await recordGoalEvent(blocked2, params);
2601
+ await updateGoalStatus(params, "blocked");
2602
+ return finishGoalRunResult(
2603
+ params,
2604
+ createGoalRunResult({
2605
+ goal: params.config.goal,
2606
+ status: "blocked",
2607
+ summary: blocked2.summary,
2608
+ events: [...events, blocked2]
2609
+ })
2610
+ );
2611
+ }
2612
+ if (llmResponse.status === "needs_user_input" && (llmResponse.toolCalls?.length ?? 0) === 0) {
2613
+ const blocked2 = createGoalEvent({
2614
+ goalId: params.config.goal.id,
2615
+ iterationId,
2616
+ kind: "goal.blocked",
2617
+ summary: "Goal needs user input but no structured question was provided",
2618
+ payload: {
2619
+ responseSummary: llmResponse.summary ?? null
2620
+ }
2621
+ });
2622
+ await recordGoalEvent(blocked2, params);
2623
+ await updateGoalStatus(params, "blocked");
2624
+ return finishGoalRunResult(
2625
+ params,
2626
+ createGoalRunResult({
2627
+ goal: params.config.goal,
2628
+ status: "blocked",
2629
+ summary: blocked2.summary,
2630
+ events: [...events, blocked2]
2631
+ })
2632
+ );
2633
+ }
2634
+ if (llmResponse.status === "blocked" && (llmResponse.toolCalls?.length ?? 0) === 0) {
2635
+ const blocked2 = createGoalEvent({
2636
+ goalId: params.config.goal.id,
2637
+ iterationId,
2638
+ kind: "goal.blocked",
2639
+ summary: llmResponse.summary || "Goal LLM reported a blocker",
2640
+ payload: {
2641
+ message: llmResponse.message?.content ?? null
2642
+ }
2643
+ });
2644
+ await recordGoalEvent(blocked2, params);
2645
+ await updateGoalStatus(params, "blocked");
2646
+ return finishGoalRunResult(
2647
+ params,
2648
+ createGoalRunResult({
2649
+ goal: params.config.goal,
2650
+ status: "blocked",
2651
+ summary: blocked2.summary,
2652
+ events: [...events, blocked2]
2653
+ })
2654
+ );
2655
+ }
2656
+ }
2657
+ if (params.config.goal.verification.commands.length > 0) {
2658
+ const checks = await runGoalVerificationChecks({
2659
+ goal: params.config.goal,
2660
+ iterationId,
2661
+ cwd: workspace,
2662
+ workspace,
2663
+ storageRoot,
2664
+ localState: params.localState,
2665
+ hostedClient: params.hostedClient
2666
+ });
2667
+ const failed = checks.find((check) => check.status !== "passed");
2668
+ const completed = createGoalEvent({
2669
+ goalId: params.config.goal.id,
2670
+ iterationId,
2671
+ kind: "iteration.completed",
2672
+ summary: failed ? "Goal iteration completed with failed checks" : "Goal verification checks passed",
2673
+ payload: {
2674
+ checks: checks.map((check) => ({
2675
+ id: check.id,
2676
+ name: check.name,
2677
+ status: check.status,
2678
+ code: check.code
2679
+ }))
2680
+ }
2681
+ });
2682
+ await recordGoalEvent(completed, params);
2683
+ await updateGoalStatus(params, failed ? "blocked" : "completed");
2684
+ return finishGoalRunResult(
2685
+ params,
2686
+ createGoalRunResult({
2687
+ goal: params.config.goal,
2688
+ status: failed ? "blocked" : "completed",
2689
+ summary: completed.summary,
2690
+ events: [...events, completed]
2691
+ })
2692
+ );
2693
+ }
2694
+ if (toolResults.length > 0) {
2695
+ const completed = createGoalEvent({
2696
+ goalId: params.config.goal.id,
2697
+ iterationId,
2698
+ kind: "iteration.completed",
2699
+ summary: llmResponse?.summary || "Goal tool calls completed",
2700
+ payload: {
2701
+ toolResults: compactToolResults(toolResults),
2702
+ verificationConfigured: false
2703
+ }
2704
+ });
2705
+ await recordGoalEvent(completed, params);
2706
+ await updateGoalStatus(params, "completed");
2707
+ return finishGoalRunResult(
2708
+ params,
2709
+ createGoalRunResult({
2710
+ goal: params.config.goal,
2711
+ status: "completed",
2712
+ summary: completed.summary,
2713
+ events: [...events, completed]
2714
+ })
2715
+ );
2716
+ }
2717
+ const blocked = createGoalEvent({
2718
+ goalId: params.config.goal.id,
2719
+ iterationId,
2720
+ kind: "goal.blocked",
2721
+ summary: "Goal produced no tool calls or verification commands",
2722
+ payload: {
2723
+ reason: "no_executable_goal_plan",
2724
+ profile: profile.id,
2725
+ next: "configure a hosted Goal LLM route, local provider, tool calls, or verification commands"
2726
+ }
2727
+ });
2728
+ await recordGoalEvent(blocked, params);
2729
+ await updateGoalStatus(params, "blocked");
2730
+ return finishGoalRunResult(
2731
+ params,
2732
+ createGoalRunResult({
2733
+ goal: params.config.goal,
2734
+ status: "blocked",
2735
+ summary: blocked.summary,
2736
+ events: [started, blocked]
2737
+ })
2738
+ );
2739
+ }
2740
+ async function runGoalPlannerLoop(params) {
2741
+ if (params.config.mode === "hosted" && !params.hostedClient) {
2742
+ return { response: null, toolResults: [] };
2743
+ }
2744
+ const messages = buildGoalLlmMessages(params.config.goal);
2745
+ const toolResults = [];
2746
+ let response = null;
2747
+ for (let round = 0; round < MAX_GOAL_TOOL_ROUNDS; round += 1) {
2748
+ response = params.config.mode === "hosted" ? await callHostedGoalLlm({
2749
+ goal: params.config.goal,
2750
+ iterationId: params.iterationId,
2751
+ hostedClient: params.hostedClient,
2752
+ localState: params.localState,
2753
+ messages,
2754
+ tools: params.toolSchemas,
2755
+ metadata: { ...params.profileMetadata, round }
2756
+ }) : await callLocalGoalLlm({
2757
+ goal: params.config.goal,
2758
+ iterationId: params.iterationId,
2759
+ localState: params.localState,
2760
+ messages,
2761
+ tools: params.toolSchemas,
2762
+ metadata: { ...params.profileMetadata, round }
2763
+ });
2764
+ if (!response) return { response: null, toolResults };
2765
+ const toolCalls = response.toolCalls ?? [];
2766
+ if (toolCalls.length === 0) return { response, toolResults };
2767
+ const roundResults = await runLlmToolCalls({
2768
+ config: params.config,
2769
+ localState: params.localState,
2770
+ hostedClient: params.hostedClient,
2771
+ iterationId: params.iterationId,
2772
+ workspace: params.workspace,
2773
+ storageRoot: params.storageRoot,
2774
+ response
2775
+ });
2776
+ toolResults.push(...roundResults);
2777
+ messages.push(assistantToolCallMessage(response));
2778
+ messages.push(...roundResults.map(toolResultMessage));
2779
+ if (roundResults.some(
2780
+ (result) => result.status === "needs_user_input" || result.status === "needs_approval"
2781
+ )) {
2782
+ return { response, toolResults };
2783
+ }
2784
+ }
2785
+ return {
2786
+ response: {
2787
+ status: "blocked",
2788
+ summary: "Goal planner exceeded tool round budget",
2789
+ message: {
2790
+ role: "assistant",
2791
+ content: "Goal planner exceeded tool round budget."
2792
+ }
2793
+ },
2794
+ toolResults
2795
+ };
2796
+ }
2797
+ function latestUnresolvedBlockedToolResult(results) {
2798
+ const latest = results.at(-1);
2799
+ return latest?.status === "blocked" ? latest : null;
2800
+ }
2801
+ async function runLlmToolCalls(params) {
2802
+ const toolCalls = params.response.toolCalls ?? [];
2803
+ const results = [];
2804
+ for (const toolCall of toolCalls) {
2805
+ const result = await runGoalLlmToolCall(
2806
+ {
2807
+ goal: params.config.goal,
2808
+ iterationId: params.iterationId,
2809
+ workspace: params.workspace,
2810
+ storageRoot: params.storageRoot,
2811
+ localState: params.localState,
2812
+ hostedClient: params.hostedClient
2813
+ },
2814
+ toolCall
2815
+ );
2816
+ results.push(result);
2817
+ if (result.status !== "ok") break;
2818
+ }
2819
+ return results;
2820
+ }
2821
+ function assistantToolCallMessage(response) {
2822
+ return {
2823
+ role: "assistant",
2824
+ content: response.message?.content ?? response.summary ?? "",
2825
+ tool_calls: (response.toolCalls ?? []).map((toolCall) => ({
2826
+ id: toolCall.id,
2827
+ type: "function",
2828
+ function: {
2829
+ name: toolCall.name,
2830
+ arguments: JSON.stringify(toolCall.arguments)
2831
+ }
2832
+ }))
2833
+ };
2834
+ }
2835
+ function toolResultMessage(result) {
2836
+ return {
2837
+ role: "tool",
2838
+ tool_call_id: result.toolCallId,
2839
+ name: result.name,
2840
+ content: JSON.stringify({
2841
+ status: result.status,
2842
+ summary: result.summary,
2843
+ output: result.output ?? {},
2844
+ checksPassed: result.checksPassed ?? null
2845
+ })
2846
+ };
2847
+ }
2848
+ function compactToolResults(results) {
2849
+ return results.map((result) => ({
2850
+ toolCallId: result.toolCallId,
2851
+ name: result.name,
2852
+ status: result.status,
2853
+ summary: result.summary,
2854
+ checksPassed: result.checksPassed
2855
+ }));
2856
+ }
2857
+ async function finishGoalRunResult(params, result) {
2858
+ if (!params.localState?.writeResult) return result;
2859
+ const current = await params.localState.get(params.config.goal.id);
2860
+ const persisted = current ? { ...result, events: current.events } : result;
2861
+ await params.localState.writeResult(params.config.goal.id, persisted);
2862
+ return persisted;
2863
+ }
2864
+ async function updateGoalStatus(params, status) {
2865
+ await params.hostedClient?.updateStatus(params.config.goal.id, status);
2866
+ if (params.localState) {
2867
+ const current = await params.localState.get(params.config.goal.id);
2868
+ await params.localState.update({
2869
+ ...current ?? params.config.goal,
2870
+ status,
2871
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2872
+ });
2873
+ }
2874
+ }
2875
+
2876
+ // src/goal/state/local.ts
2877
+ import { mkdir as mkdir3, readFile as readFile2, readdir as readdir2, writeFile as writeFile3 } from "node:fs/promises";
2878
+ import { join as join4 } from "node:path";
2879
+
2880
+ // src/goal/schemas/event.ts
2881
+ var goalEventRecordSchema = external_exports.object({
2882
+ id: external_exports.string().min(1),
2883
+ goalId: external_exports.string().min(1),
2884
+ iterationId: external_exports.string().min(1).nullable(),
2885
+ kind: external_exports.enum([
2886
+ "goal.created",
2887
+ "goal.status_changed",
2888
+ "iteration.started",
2889
+ "iteration.completed",
2890
+ "question.created",
2891
+ "question.answered",
2892
+ "approval.requested",
2893
+ "approval.decided",
2894
+ "create_pipeline.created",
2895
+ "create_pipeline.status_changed",
2896
+ "create_plan.created",
2897
+ "workflow_capture.created",
2898
+ "command.started",
2899
+ "command.completed",
2900
+ "check.completed",
2901
+ "source.updated",
2902
+ "artifact.created",
2903
+ "model.usage",
2904
+ "goal.blocked",
2905
+ "goal.failed"
2906
+ ]),
2907
+ summary: external_exports.string().min(1),
2908
+ payload: external_exports.record(external_exports.string(), external_exports.unknown()),
2909
+ createdAt: external_exports.string().min(1)
2910
+ });
2911
+ function assertGoalEventRecord(event) {
2912
+ return goalEventRecordSchema.parse(event);
2913
+ }
2914
+
2915
+ // src/goal/schemas/question.ts
2916
+ var goalQuestionOptionRecordSchema = external_exports.object({
2917
+ id: external_exports.string().min(1),
2918
+ label: external_exports.string().min(1),
2919
+ description: external_exports.string().min(1).optional()
2920
+ });
2921
+ var goalQuestionRecordSchema = external_exports.object({
2922
+ id: external_exports.string().min(1),
2923
+ goalId: external_exports.string().min(1),
2924
+ title: external_exports.string().min(1),
2925
+ reason: external_exports.string().min(1),
2926
+ required: external_exports.boolean(),
2927
+ options: external_exports.array(goalQuestionOptionRecordSchema),
2928
+ freeformAllowed: external_exports.boolean(),
2929
+ answeredAt: external_exports.string().min(1).nullable()
2930
+ });
2931
+ var goalAnswerRecordSchema = external_exports.object({
2932
+ id: external_exports.string().min(1),
2933
+ goalId: external_exports.string().min(1),
2934
+ questionId: external_exports.string().min(1),
2935
+ optionId: external_exports.string().min(1).nullable(),
2936
+ freeformText: external_exports.string().min(1).nullable(),
2937
+ value: external_exports.record(external_exports.string(), external_exports.unknown()),
2938
+ createdAt: external_exports.string().min(1)
2939
+ });
2940
+ var goalQuestionSnapshotSchema = external_exports.object({
2941
+ questions: external_exports.array(goalQuestionRecordSchema),
2942
+ answers: external_exports.array(goalAnswerRecordSchema)
2943
+ });
2944
+ function assertGoalQuestionSnapshot(snapshot) {
2945
+ return goalQuestionSnapshotSchema.parse(snapshot);
2946
+ }
2947
+
2948
+ // src/goal/schemas/result.ts
2949
+ var goalRunResultRecordSchema = external_exports.object({
2950
+ goalId: external_exports.string().min(1),
2951
+ status: external_exports.enum([
2952
+ "queued",
2953
+ "running",
2954
+ "awaiting_user_input",
2955
+ "awaiting_approval",
2956
+ "paused",
2957
+ "blocked",
2958
+ "completed",
2959
+ "failed",
2960
+ "cancelled",
2961
+ "budget_limited"
2962
+ ]),
2963
+ summary: external_exports.string().min(1),
2964
+ events: external_exports.array(goalEventRecordSchema)
2965
+ });
2966
+ function assertGoalRunResultRecord(result) {
2967
+ return goalRunResultRecordSchema.parse(result);
2968
+ }
2969
+
2970
+ // src/goal/state/local.ts
2971
+ var GOAL_STATE_FILE = "state.json";
2972
+ var GOAL_EVENTS_FILE = "events.jsonl";
2973
+ var GOAL_QUESTIONS_FILE = "questions.json";
2974
+ var GOAL_RESULT_FILE = "result.json";
2975
+ var CREATE_PIPELINE_FILE = "create-pipeline.json";
2976
+ var CREATE_PLAN_FILE = "create-plan.json";
2977
+ var WORKFLOW_CAPTURE_FILE = "workflow-capture.json";
2978
+ var LocalGoalStateAdapter = class {
2979
+ constructor(storageRoot) {
2980
+ this.storageRoot = storageRoot;
2981
+ }
2982
+ storageRoot;
2983
+ async create(goal) {
2984
+ await this.write(goal);
2985
+ return goal;
2986
+ }
2987
+ async get(goalId) {
2988
+ try {
2989
+ const raw = await readFile2(this.goalStatePath(goalId), "utf-8");
2990
+ return JSON.parse(raw);
2991
+ } catch {
2992
+ return null;
2993
+ }
2994
+ }
2995
+ async findGoalByQuestionId(questionId) {
2996
+ const root = this.goalRoot();
2997
+ let entries = [];
2998
+ try {
2999
+ entries = await readdir2(root);
3000
+ } catch {
3001
+ return null;
3002
+ }
3003
+ for (const entry of entries) {
3004
+ const goal = await this.get(entry);
3005
+ if (goal?.questions.some((question) => question.id === questionId)) {
3006
+ return goal;
3007
+ }
3008
+ }
3009
+ return null;
3010
+ }
3011
+ async update(goal) {
3012
+ const next = { ...goal, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
3013
+ await this.write(next);
3014
+ return next;
3015
+ }
3016
+ async appendEvent(goalId, event) {
3017
+ const goal = await this.requireGoal(goalId);
3018
+ const next = {
3019
+ ...goal,
3020
+ events: [...goal.events, event],
3021
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3022
+ };
3023
+ await this.write(next);
3024
+ return next;
3025
+ }
3026
+ async addQuestion(goalId, question) {
3027
+ const goal = await this.requireGoal(goalId);
3028
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3029
+ const createPipeline = goal.createPipeline ? CreatePipelineSnapshotSchema.parse({
3030
+ ...goal.createPipeline,
3031
+ state: question.required ? "awaiting_questions" : goal.createPipeline.state,
3032
+ questionIds: Array.from(
3033
+ /* @__PURE__ */ new Set([...goal.createPipeline.questionIds ?? [], question.id])
3034
+ ),
3035
+ updatedAt: now
3036
+ }) : void 0;
3037
+ const next = {
3038
+ ...goal,
3039
+ status: question.required ? "awaiting_user_input" : goal.status,
3040
+ questions: [...goal.questions, question],
3041
+ ...createPipeline ? { createPipeline } : {},
3042
+ updatedAt: now
3043
+ };
3044
+ await this.write(next);
3045
+ return next;
3046
+ }
3047
+ async answerQuestion(params) {
3048
+ const goal = await this.requireGoal(params.goalId);
3049
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3050
+ const questions = goal.questions.map(
3051
+ (question) => question.id === params.questionId ? { ...question, answeredAt: now } : question
3052
+ );
3053
+ const hasOpenRequiredQuestion = questions.some(
3054
+ (question) => question.required && !question.answeredAt
3055
+ );
3056
+ const createPipeline = goal.createPipeline && goal.createPipeline.state === "awaiting_questions" && !hasOpenRequiredQuestion ? CreatePipelineSnapshotSchema.parse({
3057
+ ...goal.createPipeline,
3058
+ state: goal.createPipeline.plan?.status === "pending_approval" ? "awaiting_plan_approval" : "planning",
3059
+ updatedAt: now
3060
+ }) : goal.createPipeline;
3061
+ const next = {
3062
+ ...goal,
3063
+ status: goal.status === "awaiting_user_input" && !hasOpenRequiredQuestion ? "queued" : goal.status,
3064
+ questions,
3065
+ answers: [...goal.answers, params.answer],
3066
+ ...createPipeline ? { createPipeline } : {},
3067
+ updatedAt: now
3068
+ };
3069
+ await this.write(next);
3070
+ return next;
3071
+ }
3072
+ async writeResult(goalId, result) {
3073
+ await mkdir3(this.goalDir(goalId), { recursive: true });
3074
+ const validated = assertGoalRunResultRecord(result);
3075
+ await writeFile3(
3076
+ this.goalResultPath(goalId),
3077
+ `${JSON.stringify(validated, null, 2)}
3078
+ `,
3079
+ "utf-8"
3080
+ );
3081
+ }
3082
+ async requireGoal(goalId) {
3083
+ const goal = await this.get(goalId);
3084
+ if (!goal) throw new Error(`goal not found: ${goalId}`);
3085
+ return goal;
3086
+ }
3087
+ goalRoot() {
3088
+ return join4(this.storageRoot, ".openpond", "goals");
3089
+ }
3090
+ goalDir(goalId) {
3091
+ return join4(this.goalRoot(), goalId);
3092
+ }
3093
+ goalStatePath(goalId) {
3094
+ return join4(this.goalDir(goalId), GOAL_STATE_FILE);
3095
+ }
3096
+ goalEventsPath(goalId) {
3097
+ return join4(this.goalDir(goalId), GOAL_EVENTS_FILE);
3098
+ }
3099
+ goalQuestionsPath(goalId) {
3100
+ return join4(this.goalDir(goalId), GOAL_QUESTIONS_FILE);
3101
+ }
3102
+ goalResultPath(goalId) {
3103
+ return join4(this.goalDir(goalId), GOAL_RESULT_FILE);
3104
+ }
3105
+ createPipelinePath(goalId) {
3106
+ return join4(this.goalDir(goalId), CREATE_PIPELINE_FILE);
3107
+ }
3108
+ createPlanPath(goalId) {
3109
+ return join4(this.goalDir(goalId), CREATE_PLAN_FILE);
3110
+ }
3111
+ workflowCapturePath(goalId) {
3112
+ return join4(this.goalDir(goalId), WORKFLOW_CAPTURE_FILE);
3113
+ }
3114
+ async write(goal) {
3115
+ await mkdir3(this.goalDir(goal.id), { recursive: true });
3116
+ const events = goal.events.map(assertGoalEventRecord);
3117
+ const questionSnapshot = assertGoalQuestionSnapshot({
3118
+ questions: goal.questions,
3119
+ answers: goal.answers
3120
+ });
3121
+ await writeFile3(
3122
+ this.goalStatePath(goal.id),
3123
+ `${JSON.stringify(goal, null, 2)}
3124
+ `,
3125
+ "utf-8"
3126
+ );
3127
+ await writeFile3(
3128
+ this.goalEventsPath(goal.id),
3129
+ events.length > 0 ? `${events.map(serializeGoalEventRecord).join("\n")}
3130
+ ` : "",
3131
+ "utf-8"
3132
+ );
3133
+ await writeFile3(
3134
+ this.goalQuestionsPath(goal.id),
3135
+ `${JSON.stringify(questionSnapshot, null, 2)}
3136
+ `,
3137
+ "utf-8"
3138
+ );
3139
+ if (goal.createPipeline) {
3140
+ await writeFile3(
3141
+ this.createPipelinePath(goal.id),
3142
+ `${JSON.stringify(goal.createPipeline, null, 2)}
3143
+ `,
3144
+ "utf-8"
3145
+ );
3146
+ if (goal.createPipeline.plan) {
3147
+ await writeFile3(
3148
+ this.createPlanPath(goal.id),
3149
+ `${JSON.stringify(goal.createPipeline.plan, null, 2)}
3150
+ `,
3151
+ "utf-8"
3152
+ );
3153
+ }
3154
+ if (goal.createPipeline.workflowCapture) {
3155
+ await writeFile3(
3156
+ this.workflowCapturePath(goal.id),
3157
+ `${JSON.stringify(goal.createPipeline.workflowCapture, null, 2)}
3158
+ `,
3159
+ "utf-8"
3160
+ );
3161
+ }
3162
+ }
3163
+ }
3164
+ };
3165
+
3166
+ // src/goal/state/hosted.ts
3167
+ function resolveHostedGoalCredential() {
3168
+ return process.env.OPENPOND_GOAL_API_KEY?.trim() || process.env.OPENPOND_API_KEY?.trim() || null;
3169
+ }
3170
+ function resolveHostedGoalApiUrl() {
3171
+ return process.env.OPENPOND_GOAL_API_URL?.trim().replace(/\/$/, "") || process.env.OPENPOND_API_URL?.trim().replace(/\/$/, "") || null;
3172
+ }
3173
+ var HostedGoalClient = class {
3174
+ constructor(apiUrl, apiKey) {
3175
+ this.apiUrl = apiUrl;
3176
+ this.apiKey = apiKey;
3177
+ }
3178
+ apiUrl;
3179
+ apiKey;
3180
+ request(path, init) {
3181
+ const headers = new Headers(init?.headers || {});
3182
+ headers.set("openpond-api-key", this.apiKey);
3183
+ return apiFetch(this.apiUrl, this.apiKey, path, {
3184
+ ...init,
3185
+ headers
3186
+ });
3187
+ }
3188
+ async getRunConfig(goalId) {
3189
+ const response = await this.request(
3190
+ `/v1/goals/${encodeURIComponent(goalId)}/run-config`
3191
+ );
3192
+ return readApiJson(response, "goal run-config");
3193
+ }
3194
+ async appendEvent(goalId, event) {
3195
+ const response = await this.request(
3196
+ `/v1/goals/${encodeURIComponent(goalId)}/events`,
3197
+ {
3198
+ method: "POST",
3199
+ body: JSON.stringify({ event })
3200
+ }
3201
+ );
3202
+ await readApiJson(response, "goal event");
3203
+ }
3204
+ async answerQuestion(params) {
3205
+ const response = await this.request(
3206
+ `/v1/goals/${encodeURIComponent(params.goalId)}/questions/${encodeURIComponent(
3207
+ params.questionId
3208
+ )}/answers`,
3209
+ {
3210
+ method: "POST",
3211
+ body: JSON.stringify({ answer: params.answer })
3212
+ }
3213
+ );
3214
+ await readApiJson(response, "goal answer");
3215
+ }
3216
+ async createQuestion(goalId, question) {
3217
+ const response = await this.request(
3218
+ `/v1/goals/${encodeURIComponent(goalId)}/questions`,
3219
+ {
3220
+ method: "POST",
3221
+ body: JSON.stringify({ question })
3222
+ }
3223
+ );
3224
+ await readApiJson(response, "goal question");
3225
+ }
3226
+ async requestApproval(request) {
3227
+ const response = await this.request(
3228
+ `/v1/goals/${encodeURIComponent(request.goalId)}/approvals`,
3229
+ {
3230
+ method: "POST",
3231
+ body: JSON.stringify({ approval: request })
3232
+ }
3233
+ );
3234
+ await readApiJson(response, "goal approval request");
3235
+ }
3236
+ async updateStatus(goalId, status) {
3237
+ const response = await this.request(
3238
+ `/v1/goals/${encodeURIComponent(goalId)}`,
3239
+ {
3240
+ method: "PATCH",
3241
+ body: JSON.stringify({ status })
3242
+ }
3243
+ );
3244
+ await readApiJson(response, "goal status update");
3245
+ }
3246
+ async pause(goalId) {
3247
+ await this.postLifecycle(goalId, "pause");
3248
+ }
3249
+ async resume(goalId) {
3250
+ await this.postLifecycle(goalId, "resume");
3251
+ }
3252
+ async cancel(goalId) {
3253
+ await this.postLifecycle(goalId, "cancel");
3254
+ }
3255
+ async approve(goalId, decisionNote) {
3256
+ await this.postLifecycle(goalId, "approve", { decisionNote });
3257
+ }
3258
+ async reject(goalId, decisionNote) {
3259
+ await this.postLifecycle(goalId, "reject", { decisionNote });
3260
+ }
3261
+ async postLifecycle(goalId, action, body) {
3262
+ const response = await this.request(
3263
+ `/v1/goals/${encodeURIComponent(goalId)}/${action}`,
3264
+ {
3265
+ method: "POST",
3266
+ body: JSON.stringify(body ?? {})
3267
+ }
3268
+ );
3269
+ await readApiJson(response, `goal ${action}`);
3270
+ }
3271
+ async callLlm(request) {
3272
+ const response = await this.request(
3273
+ `/v1/goals/${encodeURIComponent(request.goalId)}/llm`,
3274
+ {
3275
+ method: "POST",
3276
+ body: JSON.stringify(request)
3277
+ }
3278
+ );
3279
+ return readApiJson(response, "goal llm call");
3280
+ }
3281
+ async uploadArtifact(artifact) {
3282
+ const response = await this.request(
3283
+ `/v1/goals/${encodeURIComponent(artifact.goalId)}/artifacts`,
3284
+ {
3285
+ method: "POST",
3286
+ body: JSON.stringify({ artifact })
3287
+ }
3288
+ );
3289
+ return readApiJson(response, "goal artifact upload");
3290
+ }
3291
+ };
3292
+
3293
+ // src/goal/cli.ts
3294
+ var DEFAULT_HOSTED_GOAL_LAUNCH_PATH = "/run/openpond/goal-run.json";
3295
+ function printGoalHelp() {
3296
+ console.log("OpenPond Goal commands");
3297
+ console.log("");
3298
+ console.log("Usage:");
3299
+ console.log(' openpond goal "<objective>" [--cwd <path>] [--goal-storage global|workspace]');
3300
+ console.log(" openpond goal run --goal-id <id> [--cwd <path>] [--goal-storage global|workspace]");
3301
+ console.log(' openpond goal create-agent "<agent idea>" [--cwd <path>] [--goal-storage global|workspace]');
3302
+ console.log(' openpond goal update-agent "<agent change>" [--agent-id <id>] [--cwd <path>] [--goal-storage global|workspace]');
3303
+ console.log(" openpond goal plan <goal-id> [--json|--edit <instructions>] [--cwd <path>] [--goal-storage global|workspace]");
3304
+ console.log(
3305
+ " openpond goal answer <question-id> --choice <choice-id>|--answer <text> [--goal-id <id>] [--cwd <path>] [--goal-storage global|workspace]"
3306
+ );
3307
+ console.log(" openpond goal approve <goal-id> [--note <text>]");
3308
+ console.log(" openpond goal reject <goal-id> [--note <text>]");
3309
+ console.log(" openpond goal pause <goal-id>");
3310
+ console.log(" openpond goal resume <goal-id>");
3311
+ console.log(" openpond goal cancel <goal-id>");
3312
+ console.log("");
3313
+ console.log("Hosted env:");
3314
+ console.log(" OPENPOND_GOAL_API_KEY");
3315
+ console.log(" OPENPOND_GOAL_API_URL");
3316
+ console.log(" OPENPOND_GOAL_ID");
3317
+ console.log(" OPENPOND_GOAL_OUTPUT=jsonl");
3318
+ }
3319
+ function optionString(options, key) {
3320
+ const value = options[key];
3321
+ return typeof value === "string" ? value.trim() : "";
3322
+ }
3323
+ function hasFlag(options, key) {
3324
+ const value = options[key];
3325
+ return value === true || value === "true";
3326
+ }
3327
+ function requireOption(options, key, usage) {
3328
+ const value = optionString(options, key);
3329
+ if (!value) throw new Error(usage);
3330
+ return value;
3331
+ }
3332
+ function objectiveFromRest(rest, usage) {
3333
+ const objective = rest.join(" ").trim();
3334
+ if (!objective) throw new Error(usage);
3335
+ return objective;
3336
+ }
3337
+ function arrayOption(options, key) {
3338
+ const value = optionString(options, key);
3339
+ return value ? [value] : [];
3340
+ }
3341
+ function goalKindFromCommand(command) {
3342
+ if (command === "create-agent") return "create_agent";
3343
+ if (command === "update-agent") return "update_agent";
3344
+ return "general_code_goal";
3345
+ }
3346
+ async function localGoalPaths(options) {
3347
+ const cwd = optionString(options, "cwd");
3348
+ const workspace = resolveGoalWorkspace(cwd);
3349
+ const storageLocation = await resolveGoalStorageLocation(optionString(options, "goalStorage"));
3350
+ const storageRoot = resolveGoalStorageRoot({ cwd, location: storageLocation });
3351
+ return {
3352
+ workspace,
3353
+ storageRoot,
3354
+ storageLocation,
3355
+ adapter: new LocalGoalStateAdapter(storageRoot)
3356
+ };
3357
+ }
3358
+ async function readHostedGoalLaunchConfig() {
3359
+ const launchPath = process.env.OPENPOND_GOAL_RUN_CONFIG_PATH?.trim() || DEFAULT_HOSTED_GOAL_LAUNCH_PATH;
3360
+ try {
3361
+ return JSON.parse(await readFile3(launchPath, "utf8"));
3362
+ } catch {
3363
+ return null;
3364
+ }
3365
+ }
3366
+ async function resolveGoalOutputMode(params) {
3367
+ if (hasFlag(params.options, "jsonl") || optionString(params.options, "output") === "jsonl") {
3368
+ return "jsonl";
3369
+ }
3370
+ const explicit = process.env.OPENPOND_GOAL_OUTPUT?.trim();
3371
+ if (explicit === "jsonl") return "jsonl";
3372
+ return params.launchConfig?.output?.mode === "jsonl" ? "jsonl" : "json";
3373
+ }
3374
+ function printGoalRunOutput(result, mode) {
3375
+ if (mode !== "jsonl") {
3376
+ console.log(JSON.stringify(result, null, 2));
3377
+ return;
3378
+ }
3379
+ for (const event of result.events) {
3380
+ console.log(JSON.stringify({ type: "goal_event", event }));
3381
+ }
3382
+ console.log(JSON.stringify({ type: "goal_result", result }));
3383
+ }
3384
+ async function createLocalGoal(params) {
3385
+ const local = await localGoalPaths(params.options);
3386
+ let state = normalizeGoalState(
3387
+ createGoalState({
3388
+ objective: params.objective,
3389
+ kind: params.kind,
3390
+ teamId: optionString(params.options, "teamId") || null,
3391
+ projectId: optionString(params.options, "projectId") || null,
3392
+ agentId: optionString(params.options, "agentId") || null,
3393
+ workItemId: optionString(params.options, "workItemId") || null,
3394
+ conversationId: optionString(params.options, "conversationId") || null,
3395
+ verification: {
3396
+ commands: arrayOption(params.options, "verify"),
3397
+ successCriteria: arrayOption(params.options, "successCriteria")
3398
+ }
3399
+ })
3400
+ );
3401
+ if (shouldCreatePipelineForGoal(params.kind)) {
3402
+ const command = params.createPipeline?.command ?? (params.kind === "update_agent" ? "/edit" : "/create");
3403
+ const surface = params.createPipeline?.surface ?? (params.kind === "update_agent" ? "direct_prompt_edit" : "direct_prompt_create");
3404
+ const { snapshot, approval } = createInitialCreatePipeline({
3405
+ goal: state,
3406
+ command,
3407
+ surface,
3408
+ profile: params.createPipeline?.profile ?? {
3409
+ activeProfile: optionString(params.options, "profile") || null,
3410
+ repoPath: null,
3411
+ sourcePath: local.workspace,
3412
+ localHead: null
3413
+ }
3414
+ });
3415
+ state = {
3416
+ ...state,
3417
+ status: "awaiting_approval",
3418
+ approvals: [approval],
3419
+ createPipeline: snapshot,
3420
+ events: [
3421
+ ...state.events,
3422
+ createGoalEvent({
3423
+ goalId: state.id,
3424
+ kind: "create_pipeline.created",
3425
+ summary: "Create pipeline initialized",
3426
+ payload: {
3427
+ pipelineId: snapshot.id,
3428
+ requestId: snapshot.request.id,
3429
+ state: snapshot.state,
3430
+ sourceAuthority: snapshot.request.adapter.sourceAuthority
3431
+ }
3432
+ }),
3433
+ createGoalEvent({
3434
+ goalId: state.id,
3435
+ kind: "create_plan.created",
3436
+ summary: "Create plan artifact created",
3437
+ payload: {
3438
+ planId: snapshot.plan?.id ?? null,
3439
+ approvalId: approval.id,
3440
+ status: snapshot.plan?.status ?? null
3441
+ }
3442
+ }),
3443
+ createGoalEvent({
3444
+ goalId: state.id,
3445
+ kind: "workflow_capture.created",
3446
+ summary: "Workflow capture artifact created",
3447
+ payload: {
3448
+ workflowCaptureId: snapshot.workflowCapture?.id ?? null
3449
+ }
3450
+ }),
3451
+ createGoalEvent({
3452
+ goalId: state.id,
3453
+ kind: "approval.requested",
3454
+ summary: approval.title,
3455
+ payload: {
3456
+ kind: approval.kind,
3457
+ reason: approval.reason,
3458
+ requestId: snapshot.request.id,
3459
+ planId: snapshot.plan?.id ?? null
3460
+ }
3461
+ })
3462
+ ]
3463
+ };
3464
+ }
3465
+ await local.adapter.create(state);
3466
+ console.log(`Goal created: ${state.id}`);
3467
+ console.log(`State: ${goalStateDisplayPath({ storageRoot: local.storageRoot, goalId: state.id, fileName: "state.json" })}`);
3468
+ if (state.createPipeline?.plan) {
3469
+ console.log(`Plan: ${goalStateDisplayPath({ storageRoot: local.storageRoot, goalId: state.id, fileName: "create-plan.json" })}`);
3470
+ console.log(`Approval required: openpond goal approve ${state.id}`);
3471
+ }
3472
+ return state;
3473
+ }
3474
+ async function runGoal(params) {
3475
+ const launchConfig = await readHostedGoalLaunchConfig();
3476
+ const outputMode = await resolveGoalOutputMode({
3477
+ options: params.options,
3478
+ launchConfig
3479
+ });
3480
+ const credential = resolveHostedGoalCredential();
3481
+ const apiUrl = resolveHostedGoalApiUrl();
3482
+ if (credential && apiUrl) {
3483
+ const hosted = new HostedGoalClient(apiUrl, credential);
3484
+ const config = await hosted.getRunConfig(params.goalId);
3485
+ const result2 = await runGoalIteration({
3486
+ config: {
3487
+ ...config,
3488
+ iterationId: launchConfig?.iterationId ?? config.iterationId ?? null
3489
+ },
3490
+ hostedClient: hosted
3491
+ });
3492
+ printGoalRunOutput(result2, outputMode);
3493
+ return;
3494
+ }
3495
+ const local = await localGoalPaths(params.options);
3496
+ const goal = await local.adapter.get(params.goalId);
3497
+ if (!goal) throw new Error(`goal not found: ${params.goalId}`);
3498
+ const result = await runGoalIteration({
3499
+ config: {
3500
+ goal,
3501
+ mode: "local",
3502
+ workspace: local.workspace,
3503
+ storageRoot: local.storageRoot,
3504
+ iterationId: null
3505
+ },
3506
+ localState: local.adapter
3507
+ });
3508
+ printGoalRunOutput(result, outputMode);
3509
+ }
3510
+ async function answerGoal(params) {
3511
+ const optionId = optionString(params.options, "choice") || null;
3512
+ const freeformText = optionString(params.options, "answer") || null;
3513
+ if (!optionId && !freeformText) {
3514
+ throw new Error("usage: goal answer <question-id> --choice <id>|--answer <text>");
3515
+ }
3516
+ const goalId = optionString(params.options, "goalId");
3517
+ const answer = {
3518
+ id: `answer_${randomUUID9()}`,
3519
+ goalId,
3520
+ questionId: params.questionId,
3521
+ optionId,
3522
+ freeformText,
3523
+ value: {},
3524
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
3525
+ };
3526
+ const credential = resolveHostedGoalCredential();
3527
+ const apiUrl = resolveHostedGoalApiUrl();
3528
+ if (credential && apiUrl) {
3529
+ if (!goalId) {
3530
+ throw new Error("hosted goal answer requires --goal-id <goal-id>");
3531
+ }
3532
+ await new HostedGoalClient(apiUrl, credential).answerQuestion({
3533
+ goalId,
3534
+ questionId: params.questionId,
3535
+ answer
3536
+ });
3537
+ console.log(`Answered question: ${params.questionId}`);
3538
+ return;
3539
+ }
3540
+ const local = await localGoalPaths(params.options);
3541
+ const goal = goalId ? await local.adapter.get(goalId) : await local.adapter.findGoalByQuestionId(params.questionId);
3542
+ if (!goal) throw new Error(`goal not found for question: ${params.questionId}`);
3543
+ await local.adapter.answerQuestion({
3544
+ goalId: goal.id,
3545
+ questionId: params.questionId,
3546
+ answer: { ...answer, goalId: goal.id }
3547
+ });
3548
+ console.log(`Answered question: ${params.questionId}`);
3549
+ }
3550
+ async function updateGoalStatus2(params) {
3551
+ const credential = resolveHostedGoalCredential();
3552
+ const apiUrl = resolveHostedGoalApiUrl();
3553
+ if (credential && apiUrl) {
3554
+ await new HostedGoalClient(apiUrl, credential).updateStatus(
3555
+ params.goalId,
3556
+ params.status
3557
+ );
3558
+ console.log(`Goal ${params.goalId} ${params.status}`);
3559
+ return;
3560
+ }
3561
+ const local = await localGoalPaths(params.options);
3562
+ const goal = await local.adapter.get(params.goalId);
3563
+ if (!goal) throw new Error(`goal not found: ${params.goalId}`);
3564
+ await local.adapter.update({ ...goal, status: params.status });
3565
+ await local.adapter.appendEvent(
3566
+ goal.id,
3567
+ createGoalEvent({
3568
+ goalId: goal.id,
3569
+ kind: "goal.status_changed",
3570
+ summary: `Goal ${params.status}`,
3571
+ payload: {
3572
+ fromStatus: goal.status,
3573
+ toStatus: params.status
3574
+ }
3575
+ })
3576
+ );
3577
+ console.log(`Goal ${params.goalId} ${params.status}`);
3578
+ }
3579
+ async function applyGoalLifecycle(params) {
3580
+ const credential = resolveHostedGoalCredential();
3581
+ const apiUrl = resolveHostedGoalApiUrl();
3582
+ if (credential && apiUrl) {
3583
+ const client = new HostedGoalClient(apiUrl, credential);
3584
+ if (params.action === "approve") {
3585
+ await client.approve(params.goalId, optionString(params.options, "note"));
3586
+ } else if (params.action === "reject") {
3587
+ await client.reject(params.goalId, optionString(params.options, "note"));
3588
+ } else if (params.action === "pause") {
3589
+ await client.pause(params.goalId);
3590
+ } else if (params.action === "resume") {
3591
+ await client.resume(params.goalId);
3592
+ } else {
3593
+ await client.cancel(params.goalId);
3594
+ }
3595
+ const completedAction = {
3596
+ approve: "approved",
3597
+ reject: "rejected",
3598
+ pause: "paused",
3599
+ resume: "resumed",
3600
+ cancel: "cancelled"
3601
+ };
3602
+ console.log(`Goal ${params.goalId} ${completedAction[params.action]}`);
3603
+ return;
3604
+ }
3605
+ const statusByCommand = {
3606
+ pause: "paused",
3607
+ resume: "queued",
3608
+ cancel: "cancelled"
3609
+ };
3610
+ if (params.action === "approve" || params.action === "reject" || params.action === "cancel") {
3611
+ const local = await localGoalPaths(params.options);
3612
+ const goal = await local.adapter.get(params.goalId);
3613
+ if (!goal) throw new Error(`goal not found: ${params.goalId}`);
3614
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3615
+ const note = optionString(params.options, "note") || null;
3616
+ const pendingApproval = goal.approvals.find(
3617
+ (approval) => approval.status === "pending"
3618
+ );
3619
+ if (!pendingApproval && params.action === "cancel") {
3620
+ await updateGoalStatus2({
3621
+ options: params.options,
3622
+ goalId: params.goalId,
3623
+ status: "cancelled"
3624
+ });
3625
+ return;
3626
+ }
3627
+ if (!pendingApproval) {
3628
+ throw new Error(`goal has no pending approval: ${params.goalId}`);
3629
+ }
3630
+ const approvalStatus = params.action === "approve" ? "approved" : params.action === "reject" ? "rejected" : "cancelled";
3631
+ const goalStatus = params.action === "approve" ? "queued" : params.action === "reject" ? "blocked" : "cancelled";
3632
+ const approvals = goal.approvals.map(
3633
+ (approval) => approval.id === pendingApproval?.id ? {
3634
+ ...approval,
3635
+ status: approvalStatus,
3636
+ decidedAt: now,
3637
+ decisionNote: note
3638
+ } : approval
3639
+ );
3640
+ let decidedGoal = {
3641
+ ...goal,
3642
+ status: goalStatus,
3643
+ approvals,
3644
+ updatedAt: now
3645
+ };
3646
+ if (pendingApproval?.kind === "create_plan") {
3647
+ if (params.action === "approve") {
3648
+ decidedGoal = approveCreatePipelinePlan(decidedGoal, pendingApproval.id);
3649
+ } else if (params.action === "reject") {
3650
+ decidedGoal = rejectCreatePipelinePlan(decidedGoal, pendingApproval.id, note);
3651
+ } else {
3652
+ decidedGoal = cancelCreatePipelinePlan(decidedGoal, pendingApproval.id, note);
3653
+ }
3654
+ }
3655
+ await local.adapter.update(decidedGoal);
3656
+ await local.adapter.appendEvent(
3657
+ goal.id,
3658
+ createGoalEvent({
3659
+ goalId: goal.id,
3660
+ kind: "approval.decided",
3661
+ summary: pendingApproval ? `Approval ${approvalStatus}: ${pendingApproval.title}` : `Goal ${approvalStatus}`,
3662
+ payload: {
3663
+ approvalId: pendingApproval?.id ?? null,
3664
+ kind: pendingApproval?.kind ?? null,
3665
+ status: approvalStatus,
3666
+ decisionNote: note
3667
+ }
3668
+ })
3669
+ );
3670
+ if (pendingApproval?.kind === "create_plan") {
3671
+ const toState = params.action === "approve" ? "applying_source" : params.action === "reject" ? "blocked" : "cancelled";
3672
+ await local.adapter.appendEvent(
3673
+ goal.id,
3674
+ createGoalEvent({
3675
+ goalId: goal.id,
3676
+ kind: "create_pipeline.status_changed",
3677
+ summary: params.action === "approve" ? "Create plan approved" : params.action === "reject" ? "Create plan rejected" : "Create plan cancelled",
3678
+ payload: {
3679
+ fromState: goal.createPipeline?.state ?? null,
3680
+ toState,
3681
+ approvalId: pendingApproval.id
3682
+ }
3683
+ })
3684
+ );
3685
+ }
3686
+ console.log(`Goal ${params.goalId} ${approvalStatus}`);
3687
+ return;
3688
+ }
3689
+ await updateGoalStatus2({
3690
+ options: params.options,
3691
+ goalId: params.goalId,
3692
+ status: statusByCommand[params.action]
3693
+ });
3694
+ }
3695
+ async function printGoalPlan(params) {
3696
+ const credential = resolveHostedGoalCredential();
3697
+ const apiUrl = resolveHostedGoalApiUrl();
3698
+ if (credential && apiUrl) {
3699
+ const config = await new HostedGoalClient(apiUrl, credential).getRunConfig(params.goalId);
3700
+ printCreatePipeline(config.goal, hasFlag(params.options, "json"));
3701
+ return;
3702
+ }
3703
+ const local = await localGoalPaths(params.options);
3704
+ const goal = await local.adapter.get(params.goalId);
3705
+ if (!goal) throw new Error(`goal not found: ${params.goalId}`);
3706
+ printCreatePipeline(goal, hasFlag(params.options, "json"));
3707
+ }
3708
+ async function editGoalPlan(params) {
3709
+ const credential = resolveHostedGoalCredential();
3710
+ const apiUrl = resolveHostedGoalApiUrl();
3711
+ if (credential && apiUrl) {
3712
+ throw new Error("hosted create-plan editing is not available from this CLI yet");
3713
+ }
3714
+ const local = await localGoalPaths(params.options);
3715
+ const goal = await local.adapter.get(params.goalId);
3716
+ if (!goal) throw new Error(`goal not found: ${params.goalId}`);
3717
+ const previousPlanId = goal.createPipeline?.plan?.id ?? null;
3718
+ const revised = reviseCreatePipelinePlan(goal, { revision: params.revision });
3719
+ await local.adapter.update(revised);
3720
+ await local.adapter.appendEvent(
3721
+ goal.id,
3722
+ createGoalEvent({
3723
+ goalId: goal.id,
3724
+ kind: "create_plan.created",
3725
+ summary: "Create plan revised before source mutation",
3726
+ payload: {
3727
+ previousPlanId,
3728
+ planId: revised.createPipeline?.plan?.id ?? null,
3729
+ revision: params.revision
3730
+ }
3731
+ })
3732
+ );
3733
+ if (hasFlag(params.options, "json")) {
3734
+ console.log(JSON.stringify(revised.createPipeline, null, 2));
3735
+ return;
3736
+ }
3737
+ console.log(`Goal ${params.goalId} plan revised`);
3738
+ console.log(`Plan: ${revised.createPipeline?.plan?.id ?? "unknown"}`);
3739
+ }
3740
+ function printCreatePipeline(goal, json = false) {
3741
+ if (!goal?.createPipeline) {
3742
+ if (json) {
3743
+ console.log(JSON.stringify(null, null, 2));
3744
+ return;
3745
+ }
3746
+ console.log(`Goal ${goal?.id ?? "unknown"} has no create pipeline.`);
3747
+ return;
3748
+ }
3749
+ const pipeline = goal.createPipeline;
3750
+ const plan = pipeline.plan;
3751
+ if (json) {
3752
+ console.log(JSON.stringify(pipeline, null, 2));
3753
+ return;
3754
+ }
3755
+ console.log(`Goal: ${goal.id}`);
3756
+ console.log(`Pipeline: ${pipeline.state}`);
3757
+ console.log(`Request: ${pipeline.request.command} ${pipeline.request.operation}`);
3758
+ console.log(`Objective: ${pipeline.request.objective}`);
3759
+ if (!plan) return;
3760
+ console.log("");
3761
+ console.log(`Plan: ${plan.status}`);
3762
+ console.log(plan.summary);
3763
+ console.log("");
3764
+ console.log("Captured context:");
3765
+ console.log(` ${plan.capturedContextSummary}`);
3766
+ console.log("");
3767
+ console.log("Default chat action:");
3768
+ console.log(` ${plan.defaultChatAction.key ?? "none"}`);
3769
+ const actionShape = createPipelineActionShapeFromMetadata(plan.metadata);
3770
+ if (actionShape) {
3771
+ console.log("");
3772
+ console.log("Action shape:");
3773
+ console.log(` ${actionShape.label}: ${actionShape.detail}`);
3774
+ if (actionShape.directActionHint) {
3775
+ console.log(` Direct action: ${actionShape.directActionHint}`);
3776
+ }
3777
+ console.log(` Artifacts: ${actionShape.artifactPolicy}`);
3778
+ }
3779
+ console.log("");
3780
+ console.log("Source plan:");
3781
+ for (const item of plan.sourcePlan) {
3782
+ console.log(` - ${item.operation} ${item.path}: ${item.reason}`);
3783
+ }
3784
+ console.log("");
3785
+ console.log("Requirements:");
3786
+ if (plan.requirements.length === 0) {
3787
+ console.log(" - none");
3788
+ } else {
3789
+ for (const requirement of plan.requirements) {
3790
+ console.log(` - ${requirement.kind} ${requirement.name} (${requirement.status})${requirement.detail ? `: ${requirement.detail}` : ""}`);
3791
+ }
3792
+ }
3793
+ console.log("");
3794
+ console.log("Checks:");
3795
+ for (const check of plan.checks) {
3796
+ console.log(` - ${check.name}: ${check.command}`);
3797
+ }
3798
+ const pendingApproval = goal.approvals.find((approval) => approval.id === plan.approvalId);
3799
+ if (pendingApproval?.status === "pending") {
3800
+ console.log("");
3801
+ console.log(`Approve: openpond goal approve ${goal.id}`);
3802
+ console.log(`Reject: openpond goal reject ${goal.id}`);
3803
+ console.log(`Cancel: openpond goal cancel ${goal.id}`);
3804
+ }
3805
+ }
3806
+ async function runGoalCommand(options, rest) {
3807
+ const subcommand = rest[0];
3808
+ if (hasFlag(options, "help") || subcommand === "help") {
3809
+ printGoalHelp();
3810
+ return;
3811
+ }
3812
+ if (!subcommand || !["run", "create-agent", "update-agent", "plan", "answer", "approve", "reject", "pause", "resume", "cancel"].includes(subcommand)) {
3813
+ await createLocalGoal({
3814
+ options,
3815
+ objective: objectiveFromRest(rest, 'usage: goal "<objective>"'),
3816
+ kind: "general_code_goal"
3817
+ });
3818
+ return;
3819
+ }
3820
+ if (subcommand === "run") {
3821
+ await runGoal({
3822
+ options,
3823
+ goalId: requireOption(options, "goalId", "usage: goal run --goal-id <id>")
3824
+ });
3825
+ return;
3826
+ }
3827
+ if (subcommand === "plan") {
3828
+ const goalId2 = rest[1] || optionString(options, "goalId");
3829
+ if (!goalId2) throw new Error("usage: goal plan <goal-id> [--json|--edit <instructions>]");
3830
+ const revision = optionString(options, "edit") || optionString(options, "revision");
3831
+ if (revision) {
3832
+ await editGoalPlan({ options, goalId: goalId2, revision });
3833
+ return;
3834
+ }
3835
+ await printGoalPlan({ options, goalId: goalId2 });
3836
+ return;
3837
+ }
3838
+ if (subcommand === "create-agent" || subcommand === "update-agent") {
3839
+ if (subcommand === "update-agent" && !optionString(options, "agentId")) {
3840
+ throw new Error('usage: goal update-agent "<agent change>" --agent-id <id>');
3841
+ }
3842
+ await createLocalGoal({
3843
+ options,
3844
+ objective: objectiveFromRest(
3845
+ rest.slice(1),
3846
+ subcommand === "create-agent" ? 'usage: goal create-agent "<agent idea>"' : 'usage: goal update-agent "<agent change>" --agent-id <id>'
3847
+ ),
3848
+ kind: goalKindFromCommand(subcommand)
3849
+ });
3850
+ return;
3851
+ }
3852
+ if (subcommand === "answer") {
3853
+ const questionId = rest[1];
3854
+ if (!questionId) {
3855
+ throw new Error("usage: goal answer <question-id> --choice <id>|--answer <text>");
3856
+ }
3857
+ await answerGoal({ options, questionId });
3858
+ return;
3859
+ }
3860
+ const goalId = rest[1] || optionString(options, "goalId");
3861
+ if (!goalId) {
3862
+ throw new Error(`usage: goal ${subcommand} <goal-id>`);
3863
+ }
3864
+ await applyGoalLifecycle({
3865
+ options,
3866
+ goalId,
3867
+ action: subcommand
3868
+ });
3869
+ }
3870
+
3871
+ export {
3872
+ printGoalHelp,
3873
+ createLocalGoal,
3874
+ runGoalCommand
3875
+ };