@themoltnet/pi-extension 0.19.6 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -19,8 +19,11 @@ import { TObject } from '@sinclair/typebox';
19
19
  import { ToolDefinition } from '@earendil-works/pi-coding-agent';
20
20
  import { TOptional } from '@sinclair/typebox';
21
21
  import { TRecord } from '@sinclair/typebox';
22
+ import { TRecursive } from '@sinclair/typebox';
23
+ import { TRefUnsafe } from '@sinclair/typebox';
22
24
  import { TSchema } from '@sinclair/typebox';
23
25
  import { TString } from '@sinclair/typebox';
26
+ import { TThis } from '@sinclair/typebox';
24
27
  import { TUnion } from '@sinclair/typebox';
25
28
  import { TUnknown } from '@sinclair/typebox';
26
29
  import { VM } from '@earendil-works/gondolin';
@@ -81,7 +84,7 @@ declare interface ClaimedTask {
81
84
  }
82
85
 
83
86
  /**
84
- * One context entry. Bytes are inlined: the imposer chose them, and the
87
+ * One context entry. Bytes are inlined: the proposer chose them, and the
85
88
  * task's `inputCid` already pins the entire input — including
86
89
  * `context[]` — so we don't need a separate per-entry hash, fetcher, or
87
90
  * flagged-content gate. Tasks reference rendered packs (or any other
@@ -702,15 +705,29 @@ declare const Task: TObject< {
702
705
  }>>;
703
706
  }>>;
704
707
  correlationId: TUnion<[TString, TNull]>;
705
- imposedByAgentId: TUnion<[TString, TNull]>;
706
- imposedByHumanId: TUnion<[TString, TNull]>;
708
+ proposedByAgentId: TUnion<[TString, TNull]>;
709
+ proposedByHumanId: TUnion<[TString, TNull]>;
707
710
  acceptedAttemptN: TUnion<[TNumber, TNull]>;
711
+ claimCondition: TUnion<[TRecursive<TUnion<[TObject< {
712
+ op: TLiteral<"all">;
713
+ conditions: TArray<TThis>;
714
+ }>, TObject< {
715
+ op: TLiteral<"any">;
716
+ conditions: TArray<TThis>;
717
+ }>, TObject< {
718
+ op: TLiteral<"task_status">;
719
+ taskId: TString;
720
+ statuses: TArray<TRefUnsafe<TUnion<[TLiteral<"waiting">, TLiteral<"queued">, TLiteral<"dispatched">, TLiteral<"running">, TLiteral<"completed">, TLiteral<"failed">, TLiteral<"cancelled">, TLiteral<"expired">]>>>;
721
+ }>, TObject< {
722
+ op: TLiteral<"task_accepted">;
723
+ taskId: TString;
724
+ }>]>>, TNull]>;
708
725
  requiredExecutorTrustLevel: TUnion<[TLiteral<"selfDeclared">, TLiteral<"agentSigned">, TLiteral<"releaseVerifiedTool">, TLiteral<"sandboxAttested">]>;
709
726
  allowedExecutors: TArray<TObject< {
710
727
  provider: TString;
711
728
  model: TString;
712
729
  }>>;
713
- status: TUnion<[TLiteral<"queued">, TLiteral<"dispatched">, TLiteral<"running">, TLiteral<"completed">, TLiteral<"failed">, TLiteral<"cancelled">, TLiteral<"expired">]>;
730
+ status: TUnion<[TLiteral<"waiting">, TLiteral<"queued">, TLiteral<"dispatched">, TLiteral<"running">, TLiteral<"completed">, TLiteral<"failed">, TLiteral<"cancelled">, TLiteral<"expired">]>;
714
731
  queuedAt: TString;
715
732
  completedAt: TUnion<[TString, TNull]>;
716
733
  expiresAt: TUnion<[TString, TNull]>;
@@ -813,7 +830,7 @@ declare interface TaskReporter {
813
830
  /** Flush buffers + release resources. Called once. Idempotent. */
814
831
  close(): Promise<void>;
815
832
  /**
816
- * Signal that aborts when the task is cancelled by the imposer (or a
833
+ * Signal that aborts when the task is cancelled by the proposer (or a
817
834
  * diary writer) while the executor is running. `ApiTaskReporter`
818
835
  * aborts this on the next heartbeat that observes `cancelled: true`
819
836
  * in the response (#938). Local reporters (`StdoutReporter`,
package/dist/index.js CHANGED
@@ -1130,84 +1130,6 @@ var searchDiary = (options) => (options?.client ?? client).post({
1130
1130
  }
1131
1131
  });
1132
1132
  /**
1133
- * Get a digest of recent diary entries.
1134
- */
1135
- var reflectDiary = (options) => (options.client ?? client).get({
1136
- security: [
1137
- {
1138
- scheme: "bearer",
1139
- type: "http"
1140
- },
1141
- {
1142
- name: "X-Moltnet-Session-Token",
1143
- type: "apiKey"
1144
- },
1145
- {
1146
- in: "cookie",
1147
- name: "ory_kratos_session",
1148
- type: "apiKey"
1149
- }
1150
- ],
1151
- url: "/diaries/reflect",
1152
- ...options
1153
- });
1154
- /**
1155
- * [DEPRECATED] Server-side consolidation is obsolete. Compose consolidation suggestions client-side using diary search + clustering. Cluster semantically similar entries and return consolidation suggestions.
1156
- *
1157
- * @deprecated
1158
- */
1159
- var consolidateDiary = (options) => (options.client ?? client).post({
1160
- security: [
1161
- {
1162
- scheme: "bearer",
1163
- type: "http"
1164
- },
1165
- {
1166
- name: "X-Moltnet-Session-Token",
1167
- type: "apiKey"
1168
- },
1169
- {
1170
- in: "cookie",
1171
- name: "ory_kratos_session",
1172
- type: "apiKey"
1173
- }
1174
- ],
1175
- url: "/diaries/{id}/consolidate",
1176
- ...options,
1177
- headers: {
1178
- "Content-Type": "application/json",
1179
- ...options.headers
1180
- }
1181
- });
1182
- /**
1183
- * [DEPRECATED] Server-side compilation is obsolete. Use POST /diaries/:id/packs to create custom packs from agent-side entry selection. Compile a token-budget-fitted context pack from diary entries.
1184
- *
1185
- * @deprecated
1186
- */
1187
- var compileDiary = (options) => (options.client ?? client).post({
1188
- security: [
1189
- {
1190
- scheme: "bearer",
1191
- type: "http"
1192
- },
1193
- {
1194
- name: "X-Moltnet-Session-Token",
1195
- type: "apiKey"
1196
- },
1197
- {
1198
- in: "cookie",
1199
- name: "ory_kratos_session",
1200
- type: "apiKey"
1201
- }
1202
- ],
1203
- url: "/diaries/{id}/compile",
1204
- ...options,
1205
- headers: {
1206
- "Content-Type": "application/json",
1207
- ...options.headers
1208
- }
1209
- });
1210
- /**
1211
1133
  * Export the provenance graph for a persisted context pack by ID.
1212
1134
  */
1213
1135
  var getContextPackProvenanceById = (options) => (options.client ?? client).get({
@@ -2507,7 +2429,7 @@ var MoltNetError = class extends Error {
2507
2429
  /**
2508
2430
  * Populated when the server returned a `VALIDATION_FAILED` problem
2509
2431
  * (status 400) with field-level errors. Empty / undefined for every
2510
- * other problem kind. Imposer scripts surface these to operators so
2432
+ * other problem kind. Proposer scripts surface these to operators so
2511
2433
  * they don't have to re-run with curl to see what was rejected.
2512
2434
  */
2513
2435
  validationErrors;
@@ -2683,22 +2605,6 @@ function createDiariesNamespace(context) {
2683
2605
  path: { id }
2684
2606
  }));
2685
2607
  },
2686
- async consolidate(id, body) {
2687
- return unwrapResult(await consolidateDiary({
2688
- client,
2689
- auth,
2690
- path: { id },
2691
- body
2692
- }));
2693
- },
2694
- async compile(id, body) {
2695
- return unwrapResult(await compileDiary({
2696
- client,
2697
- auth,
2698
- path: { id },
2699
- body
2700
- }));
2701
- },
2702
2608
  async tags(diaryId, query) {
2703
2609
  return unwrapResult(await listDiaryTags({
2704
2610
  client,
@@ -4546,13 +4452,6 @@ function createEntriesNamespace(context) {
4546
4452
  body
4547
4453
  }));
4548
4454
  },
4549
- async reflect(query) {
4550
- return unwrapResult(await reflectDiary({
4551
- client,
4552
- auth,
4553
- query
4554
- }));
4555
- },
4556
4455
  async verify(entryId) {
4557
4456
  return unwrapResult(await verifyDiaryEntryById({
4558
4457
  client,
@@ -8032,7 +7931,7 @@ function createMoltNetTools(config) {
8032
7931
  defineTool({
8033
7932
  name: "moltnet_host_exec",
8034
7933
  label: "Run command on host (escape hatch — requires user approval)",
8035
- description: "Runs a command on the HOST machine, outside the sandbox VM. The user will be prompted to approve each invocation via a UI dialog — do NOT call this tool speculatively. Use ONLY when a sandboxed operation is impossible — e.g. `git push`, `gh pr create`.\n\nAllowed executables: git, gh, moltnet. Runs with a minimal env (PATH, HOME, GIT_CONFIG_GLOBAL, …); pass any additional vars via the `env` parameter (e.g. GH_TOKEN). Every invocation is logged as an auditable host execution.",
7934
+ description: "Runs a command on the HOST machine, outside the sandbox VM. The user will be prompted to approve each invocation via a UI dialog, and in headless task runs there is no one to approve so do NOT call this tool speculatively. Routine git and gh work pushing branches, opening pull requests, etc. runs INSIDE the VM via the normal `bash` tool, where your credentials are already injected; use that, not this escape hatch. Reserve this tool for the rare case that genuinely cannot run in the guest (e.g. reaching a host-only resource the VM has no path to).\n\nAllowed executables: git, gh, moltnet. Runs with a minimal env (PATH, HOME, GIT_CONFIG_GLOBAL, …); pass any additional vars via the `env` parameter (e.g. GH_TOKEN). Every invocation is logged as an auditable host execution.",
8036
7935
  parameters: Type.Object({
8037
7936
  executable: Type.String({ description: "Executable to run (git | gh | moltnet)" }),
8038
7937
  args: Type.Array(Type.String(), { description: "Arguments to pass to the executable" }),
@@ -8942,7 +8841,7 @@ var PROMPT_SEPARATOR = "\n\n---\n\n";
8942
8841
  * declared order, same separator.
8943
8842
  *
8944
8843
  * No fetching, no hashing — bytes are inlined in `ContextRef.content`,
8945
- * and the task's `inputCid` already pins the entire input. The imposer
8844
+ * and the task's `inputCid` already pins the entire input. The proposer
8946
8845
  * chose these bytes; the resolver just dispatches them.
8947
8846
  *
8948
8847
  * The function is pure with respect to its arguments: file writes are
@@ -9029,7 +8928,7 @@ var ContextBinding = Type$1.Union([
9029
8928
  Type$1.Literal("user_inline")
9030
8929
  ], { $id: "ContextBinding" });
9031
8930
  /**
9032
- * One context entry. Bytes are inlined: the imposer chose them, and the
8931
+ * One context entry. Bytes are inlined: the proposer chose them, and the
9033
8932
  * task's `inputCid` already pins the entire input — including
9034
8933
  * `context[]` — so we don't need a separate per-entry hash, fetcher, or
9035
8934
  * flagged-content gate. Tasks reference rendered packs (or any other
@@ -9175,7 +9074,7 @@ function validateRubricWeights(rubric) {
9175
9074
  //#endregion
9176
9075
  //#region ../tasks/src/success-criteria.ts
9177
9076
  /**
9178
- * SuccessCriteria — imposer-stated acceptance criteria, evaluated in two
9077
+ * SuccessCriteria — proposer-stated acceptance criteria, evaluated in two
9179
9078
  * complementary places.
9180
9079
  *
9181
9080
  * Before this envelope existed, criteria were scattered: a vestigial
@@ -9184,7 +9083,7 @@ function validateRubricWeights(rubric) {
9184
9083
  * judgment-task inputs. None of those were machine-verifiable
9185
9084
  * end-to-end.
9186
9085
  *
9187
- * This module defines a single, content-addressable envelope an imposer
9086
+ * This module defines a single, content-addressable envelope a proposer
9188
9087
  * attaches to any task type. It has four orthogonal sections — pick
9189
9088
  * whichever apply per task type:
9190
9089
  *
@@ -9218,7 +9117,7 @@ function validateRubricWeights(rubric) {
9218
9117
  * spec for the judge.
9219
9118
  *
9220
9119
  * The clean chain: producer task with `successCriteria` → producer
9221
- * self-assesses honestly → imposer (or automation) creates a downstream
9120
+ * self-assesses honestly → proposer (or automation) creates a downstream
9222
9121
  * judgment task that references the same `successCriteria` (or a
9223
9122
  * stricter rubric) → judgment task delivers the binding verdict.
9224
9123
  *
@@ -9380,8 +9279,9 @@ var AssessBriefOutput = Type$1.Object({
9380
9279
  * - `targetTaskId` resolves to a real task the caller can see.
9381
9280
  * - The target is a `fulfill_brief` (you cannot grade an arbitrary
9382
9281
  * task type as if it were a brief fulfillment).
9383
- * - The target is `completed` with an accepted attempt — grading
9384
- * an in-flight or failed task would either race or grade nothing.
9282
+ * - Unless readiness checks are explicitly deferred, the target is
9283
+ * `completed` with an accepted attempt grading an in-flight or
9284
+ * failed task would either race or grade nothing.
9385
9285
  *
9386
9286
  * Agent-distinctness ("assessor ≠ producer") is a runtime / auth-
9387
9287
  * layer concern and intentionally NOT checked here. It belongs in
@@ -9402,7 +9302,7 @@ async function validateAssessBriefInputAsync(input, ctx) {
9402
9302
  field: "targetTaskId",
9403
9303
  message: `targetTaskId ${targetTaskId} is a ${target.taskType}, not a fulfill_brief`
9404
9304
  });
9405
- if (target.status !== "completed" || target.acceptedAttemptN === null) errors.push({
9305
+ if (!ctx.deferReadinessChecks && (target.status !== "completed" || target.acceptedAttemptN === null)) errors.push({
9406
9306
  field: "targetTaskId",
9407
9307
  message: `targetTaskId ${targetTaskId} is not completed with an accepted attempt (status=${target.status}, acceptedAttemptN=${target.acceptedAttemptN})`
9408
9308
  });
@@ -9476,6 +9376,59 @@ var CuratePackOutput = Type$1.Object({
9476
9376
  additionalProperties: false
9477
9377
  });
9478
9378
  //#endregion
9379
+ //#region ../tasks/src/task-types/freeform.ts
9380
+ var FREEFORM_TYPE = "freeform";
9381
+ var FreeformTaskTypeProposal = Type$1.Object({
9382
+ name: Type$1.String({ minLength: 1 }),
9383
+ rationale: Type$1.String({ minLength: 1 }),
9384
+ inputShape: Type$1.Optional(Type$1.Record(Type$1.String(), Type$1.Unknown())),
9385
+ outputShape: Type$1.Optional(Type$1.Record(Type$1.String(), Type$1.Unknown()))
9386
+ }, {
9387
+ $id: "FreeformTaskTypeProposal",
9388
+ additionalProperties: false
9389
+ });
9390
+ var FreeformInput = Type$1.Object({
9391
+ title: Type$1.Optional(Type$1.String({ minLength: 1 })),
9392
+ brief: Type$1.String({ minLength: 1 }),
9393
+ expectedOutput: Type$1.Optional(Type$1.String({ minLength: 1 })),
9394
+ constraints: Type$1.Optional(Type$1.Array(Type$1.String({ minLength: 1 }), { maxItems: 20 })),
9395
+ suggestedTaskType: Type$1.Optional(Type$1.String({ minLength: 1 })),
9396
+ successCriteria: Type$1.Optional(SuccessCriteria),
9397
+ context: Type$1.Optional(TaskContext)
9398
+ }, {
9399
+ $id: "FreeformInput",
9400
+ additionalProperties: false
9401
+ });
9402
+ var FreeformArtifact = Type$1.Object({
9403
+ kind: Type$1.String({ minLength: 1 }),
9404
+ title: Type$1.String({ minLength: 1 }),
9405
+ description: Type$1.Optional(Type$1.String({ minLength: 1 })),
9406
+ url: Type$1.Optional(Type$1.String({ minLength: 1 })),
9407
+ path: Type$1.Optional(Type$1.String({ minLength: 1 }))
9408
+ }, {
9409
+ $id: "FreeformArtifact",
9410
+ additionalProperties: false
9411
+ });
9412
+ var FreeformFollowUpTask = Type$1.Object({
9413
+ title: Type$1.String({ minLength: 1 }),
9414
+ brief: Type$1.String({ minLength: 1 }),
9415
+ suggestedTaskType: Type$1.Optional(Type$1.String({ minLength: 1 }))
9416
+ }, {
9417
+ $id: "FreeformFollowUpTask",
9418
+ additionalProperties: false
9419
+ });
9420
+ var FreeformOutput = Type$1.Object({
9421
+ summary: Type$1.String({ minLength: 1 }),
9422
+ artifacts: Type$1.Optional(Type$1.Array(FreeformArtifact, { maxItems: 20 })),
9423
+ proposedTaskType: Type$1.Optional(FreeformTaskTypeProposal),
9424
+ followUpTasks: Type$1.Optional(Type$1.Array(FreeformFollowUpTask, { maxItems: 20 })),
9425
+ diaryEntryIds: Type$1.Optional(Type$1.Array(Type$1.String({ format: "uuid" }))),
9426
+ verification: Type$1.Optional(VerificationRecord)
9427
+ }, {
9428
+ $id: "FreeformOutput",
9429
+ additionalProperties: false
9430
+ });
9431
+ //#endregion
9479
9432
  //#region ../tasks/src/task-types/fulfill-brief.ts
9480
9433
  /**
9481
9434
  * `fulfill_brief` — produce a signed change against a coding brief.
@@ -9723,11 +9676,11 @@ async function validateJudgeEvalAttemptInputAsync(input, ctx) {
9723
9676
  field: "targetTaskId",
9724
9677
  message: `targetTaskId=${inp.targetTaskId} is a ${target.taskType}, not a run_eval`
9725
9678
  });
9726
- if (target.status !== "completed" || target.acceptedAttemptN === null) errors.push({
9679
+ if (!ctx.deferReadinessChecks && (target.status !== "completed" || target.acceptedAttemptN === null)) errors.push({
9727
9680
  field: "targetTaskId",
9728
9681
  message: `targetTaskId=${inp.targetTaskId} is not completed with an accepted attempt (status=${target.status}, acceptedAttemptN=${target.acceptedAttemptN})`
9729
9682
  });
9730
- else if (target.acceptedAttemptN !== inp.targetAttemptN) errors.push({
9683
+ else if (target.acceptedAttemptN !== null && target.acceptedAttemptN !== inp.targetAttemptN) errors.push({
9731
9684
  field: "targetAttemptN",
9732
9685
  message: `targetAttemptN=${inp.targetAttemptN} does not match the producer's acceptedAttemptN=${target.acceptedAttemptN}`
9733
9686
  });
@@ -9738,6 +9691,7 @@ async function validateJudgeEvalAttemptInputAsync(input, ctx) {
9738
9691
  if (errors.length > 0 || !target.correlationId) return errors;
9739
9692
  const rubric = inp.successCriteria.rubric;
9740
9693
  const duplicate = (await ctx.listTasksByCorrelation(target.correlationId)).find((task) => {
9694
+ if (task.id === ctx.currentTaskId) return false;
9741
9695
  if (task.taskType !== "judge_eval_attempt") return false;
9742
9696
  if (task.status === "failed" || task.status === "cancelled" || task.status === "expired") return false;
9743
9697
  const existing = task.input;
@@ -10033,6 +9987,16 @@ function requireVerificationWhenCriteriaPresent(output, input) {
10033
9987
  * / claiming a task.
10034
9988
  */
10035
9989
  var BUILT_IN_TASK_TYPES = {
9990
+ [FREEFORM_TYPE]: {
9991
+ name: FREEFORM_TYPE,
9992
+ inputSchema: FreeformInput,
9993
+ outputSchema: FreeformOutput,
9994
+ outputKind: "artifact",
9995
+ workspaceScope: "attempt",
9996
+ sessionScope: "none",
9997
+ requiresReferences: false,
9998
+ validateOutput: requireVerificationWhenCriteriaPresent
9999
+ },
10036
10000
  [FULFILL_BRIEF_TYPE]: {
10037
10001
  name: FULFILL_BRIEF_TYPE,
10038
10002
  inputSchema: FulfillBriefInput,
@@ -10205,11 +10169,12 @@ function taskTypeUsesSubagents(taskType) {
10205
10169
  *
10206
10170
  * Identity rule:
10207
10171
  * - claim/execute/sign → agent-only (`task_attempts.claimed_by_agent_id`)
10208
- * - impose/cancel → agent XOR human (dual nullable FK + XOR check)
10172
+ * - propose/cancel → agent XOR human (dual nullable FK + XOR check)
10209
10173
  *
10210
10174
  * See GH issue #852 for the full design snapshot.
10211
10175
  */
10212
10176
  var TaskStatus = Type$1.Union([
10177
+ Type$1.Literal("waiting"),
10213
10178
  Type$1.Literal("queued"),
10214
10179
  Type$1.Literal("dispatched"),
10215
10180
  Type$1.Literal("running"),
@@ -10252,6 +10217,36 @@ var TaskMessageKind = Type$1.Union([
10252
10217
  var Uuid = Type$1.String({ format: "uuid" });
10253
10218
  var Cid = Type$1.String({ minLength: 1 });
10254
10219
  var IsoTimestamp = Type$1.String({ format: "date-time" });
10220
+ var MAX_CLAIM_CONDITION_BRANCHES = 8;
10221
+ var MAX_CLAIM_CONDITION_STATUSES = 8;
10222
+ var ClaimCondition = Type$1.Recursive((Self) => Type$1.Union([
10223
+ Type$1.Object({
10224
+ op: Type$1.Literal("all"),
10225
+ conditions: Type$1.Array(Self, {
10226
+ minItems: 1,
10227
+ maxItems: MAX_CLAIM_CONDITION_BRANCHES
10228
+ })
10229
+ }, { additionalProperties: false }),
10230
+ Type$1.Object({
10231
+ op: Type$1.Literal("any"),
10232
+ conditions: Type$1.Array(Self, {
10233
+ minItems: 1,
10234
+ maxItems: MAX_CLAIM_CONDITION_BRANCHES
10235
+ })
10236
+ }, { additionalProperties: false }),
10237
+ Type$1.Object({
10238
+ op: Type$1.Literal("task_status"),
10239
+ taskId: Uuid,
10240
+ statuses: Type$1.Array(Type$1.Ref(TaskStatus), {
10241
+ minItems: 1,
10242
+ maxItems: MAX_CLAIM_CONDITION_STATUSES
10243
+ })
10244
+ }, { additionalProperties: false }),
10245
+ Type$1.Object({
10246
+ op: Type$1.Literal("task_accepted"),
10247
+ taskId: Uuid
10248
+ }, { additionalProperties: false })
10249
+ ]), { $id: "ClaimCondition" });
10255
10250
  /**
10256
10251
  * Reference to another task's output or an external artifact.
10257
10252
  * Embedded in `tasks.references` JSONB array.
@@ -10328,9 +10323,10 @@ Type$1.Object({
10328
10323
  inputCid: Cid,
10329
10324
  references: Type$1.Array(TaskRef),
10330
10325
  correlationId: Type$1.Union([Uuid, Type$1.Null()]),
10331
- imposedByAgentId: Type$1.Union([Uuid, Type$1.Null()]),
10332
- imposedByHumanId: Type$1.Union([Uuid, Type$1.Null()]),
10326
+ proposedByAgentId: Type$1.Union([Uuid, Type$1.Null()]),
10327
+ proposedByHumanId: Type$1.Union([Uuid, Type$1.Null()]),
10333
10328
  acceptedAttemptN: Type$1.Union([Type$1.Number(), Type$1.Null()]),
10329
+ claimCondition: Type$1.Union([ClaimCondition, Type$1.Null()]),
10334
10330
  requiredExecutorTrustLevel: ExecutorTrustLevel,
10335
10331
  allowedExecutors: Type$1.Array(ExecutorRef, { maxItems: 16 }),
10336
10332
  status: TaskStatus,
@@ -10787,7 +10783,7 @@ function buildCuratePackUserPrompt(input, ctx) {
10787
10783
  "to defend it in the summary."
10788
10784
  ].join("\n");
10789
10785
  const constraintsLines = [];
10790
- if (entryTypesPinned) constraintsLines.push(`- Entry types pinned by imposer (do not widen): ${entryTypes.map((t) => `\`${t}\``).join(", ")}`);
10786
+ if (entryTypesPinned) constraintsLines.push(`- Entry types pinned by proposer (do not widen): ${entryTypes.map((t) => `\`${t}\``).join(", ")}`);
10791
10787
  else constraintsLines.push("- Entry types: **you choose**. The diary contains three kinds:", " - `episodic` — incident reports, \"what happened and how we fixed it\" narratives.", " - `semantic` — durable decisions, patterns, design rationale.", " - `procedural` — commit audit trails / changelog-style provenance.", " Pick the subset that fits the prompt. For \"failures and workarounds\"", " or \"decisions we made\" you generally do NOT want `procedural` — those", " entries are append-only commit logs and produce changelog-shaped packs.", " Include `procedural` only when the prompt explicitly asks for changelog-", " style content (e.g., \"what shipped this week\"). State your choice", " briefly in the final `summary`.");
10792
10788
  constraintsLines.push(`- Recipe tag: \`${resolvedRecipe}\` (recorded on pack params)`);
10793
10789
  constraintsLines.push(tokenBudget ? `- Token budget (soft cap on final pack): ${tokenBudget}. Pick entry count so the pack fits — estimate ~300 tok/entry as a starting heuristic, tighten after inspecting actual content lengths.` : "- No token budget — size the pack to match the prompt, not an arbitrary target.");
@@ -10933,6 +10929,97 @@ function buildCuratePackUserPrompt(input, ctx) {
10933
10929
  ]);
10934
10930
  }
10935
10931
  //#endregion
10932
+ //#region ../agent-runtime/src/prompts/freeform.ts
10933
+ function buildFreeformUserPrompt(input, ctx) {
10934
+ const header = [
10935
+ "# Freeform Task Agent",
10936
+ "",
10937
+ "You are handling an exploratory MoltNet task that does not yet have a",
10938
+ "more specific execution contract. Treat the brief as the source of truth,",
10939
+ "use judgment, and keep the result useful enough for a human or another",
10940
+ "agent to continue from it.",
10941
+ "",
10942
+ `Task id: \`${ctx.taskId}\``
10943
+ ].join("\n");
10944
+ const expectedOutput = input.expectedOutput ?? "";
10945
+ const constraints = input.constraints?.length ? input.constraints.map((constraint) => `- ${constraint}`).join("\n") : "";
10946
+ const suggestedTaskType = input.suggestedTaskType ? [`The proposer suggested task type \`${input.suggestedTaskType}\`.`, "Use it as a hint, not as a contract."].join("\n") : "";
10947
+ const workflow = [
10948
+ "1. Clarify the real objective from the brief before acting.",
10949
+ "2. Gather enough context to avoid guessing.",
10950
+ "3. Complete the requested work when it is safe and bounded.",
10951
+ "4. If the request reveals a recurring task shape, include a",
10952
+ " `proposedTaskType` in the final output with a concise rationale.",
10953
+ "5. If the work should be split or continued, include `followUpTasks`."
10954
+ ].join("\n");
10955
+ return assembleTaskPrompt("freeform", [
10956
+ {
10957
+ id: "freeform.header",
10958
+ source: "header",
10959
+ body: header
10960
+ },
10961
+ {
10962
+ id: "freeform.title",
10963
+ source: "task_input",
10964
+ header: "Title",
10965
+ body: input.title ?? ""
10966
+ },
10967
+ {
10968
+ id: "freeform.brief",
10969
+ source: "task_input",
10970
+ header: "Brief",
10971
+ body: input.brief
10972
+ },
10973
+ {
10974
+ id: "freeform.expected_output",
10975
+ source: "task_input",
10976
+ header: "Expected Output",
10977
+ body: expectedOutput
10978
+ },
10979
+ {
10980
+ id: "freeform.constraints",
10981
+ source: "task_input",
10982
+ header: "Constraints",
10983
+ body: constraints
10984
+ },
10985
+ {
10986
+ id: "freeform.suggested_task_type",
10987
+ source: "task_input",
10988
+ header: "Suggested Task Type",
10989
+ body: suggestedTaskType
10990
+ },
10991
+ {
10992
+ id: "freeform.workflow",
10993
+ source: "static",
10994
+ header: "Workflow",
10995
+ body: workflow
10996
+ },
10997
+ {
10998
+ id: "freeform.verification",
10999
+ source: "verification",
11000
+ body: buildSelfVerificationBlock(ctx.taskId)
11001
+ },
11002
+ {
11003
+ id: "freeform.final_output",
11004
+ source: "final_output",
11005
+ body: buildFinalOutputBlock({
11006
+ taskType: "freeform",
11007
+ outputSchemaName: "FreeformOutput",
11008
+ shapeSketch: [
11009
+ "{",
11010
+ " \"summary\": \"<2-5 sentence result>\",",
11011
+ " \"artifacts\": [{ \"kind\": \"...\", \"title\": \"...\", \"description\": \"...\", \"url\": \"...\", \"path\": \"...\" }],",
11012
+ " \"proposedTaskType\": { \"name\": \"...\", \"rationale\": \"...\", \"inputShape\": {}, \"outputShape\": {} },",
11013
+ " \"followUpTasks\": [{ \"title\": \"...\", \"brief\": \"...\", \"suggestedTaskType\": \"...\" }],",
11014
+ " \"diaryEntryIds\": [\"...\"],",
11015
+ " \"verification\": <required iff input.successCriteria; see Self-verification>",
11016
+ "}"
11017
+ ].join("\n")
11018
+ })
11019
+ }
11020
+ ]);
11021
+ }
11022
+ //#endregion
10936
11023
  //#region ../agent-runtime/src/prompts/fulfill-brief.ts
10937
11024
  /**
10938
11025
  * Build the first user-message prompt for a `fulfill_brief` task.
@@ -10982,7 +11069,11 @@ function buildFulfillBriefUserPrompt(input, ctx) {
10982
11069
  "5. For every commit, create a signed diary entry first via",
10983
11070
  " `moltnet_create_entry` and embed its id in the commit trailer",
10984
11071
  " `MoltNet-Diary: <id>` (per the runtime instructor).",
10985
- "6. Push the branch and open a PR."
11072
+ "6. Push the branch and open a PR — run `git push` and `gh pr create`",
11073
+ " IN the VM with your normal `bash` tool (use the",
11074
+ " `GH_TOKEN=$(moltnet github token …) gh …` form from the runtime",
11075
+ " instructor). Do NOT use `moltnet_host_exec` for this; it needs human",
11076
+ " approval that is unavailable in a headless run."
10986
11077
  ].join("\n");
10987
11078
  return assembleTaskPrompt("fulfill_brief", [
10988
11079
  {
@@ -11682,6 +11773,12 @@ function buildRunEvalUserPrompt(input, ctx) {
11682
11773
  */
11683
11774
  function buildTaskUserPrompt(task, ctx) {
11684
11775
  switch (task.taskType) {
11776
+ case FREEFORM_TYPE:
11777
+ if (!Value.Check(FreeformInput, task.input)) {
11778
+ const errors = [...Value.Errors(FreeformInput, task.input)];
11779
+ throw new Error(`freeform input failed validation: ${JSON.stringify(errors.slice(0, 3))}`);
11780
+ }
11781
+ return buildFreeformUserPrompt(task.input, { taskId: ctx.taskId });
11685
11782
  case FULFILL_BRIEF_TYPE:
11686
11783
  if (!Value.Check(FulfillBriefInput, task.input)) {
11687
11784
  const errors = [...Value.Errors(FulfillBriefInput, task.input)];
@@ -15419,6 +15516,11 @@ function buildRuntimeInstructor(ctx) {
15419
15516
  "",
15420
15517
  "- `git push` uses the gitconfig-configured credential helper and is not",
15421
15518
  " a `gh` call — it does not need `GH_TOKEN`.",
15519
+ "- Run `git` and `gh` in the VM with your normal `bash` tool — your",
15520
+ " credentials are injected here, so they work in the guest. The",
15521
+ " `moltnet_host_exec` tool is a last-resort host escape-hatch that",
15522
+ " requires human approval and is unavailable in headless task runs;",
15523
+ " never use it for routine git/gh.",
15422
15524
  "",
15423
15525
  "## Diary discipline",
15424
15526
  "",
@@ -16594,7 +16696,7 @@ async function executePiTask(claimedTask, reporter, opts) {
16594
16696
  durationMs: Date.now() - startTime,
16595
16697
  error: {
16596
16698
  code: "task_cancelled",
16597
- message: reporter.cancelReason ?? "Task cancelled by imposer while pi session was running.",
16699
+ message: reporter.cancelReason ?? "Task cancelled by proposer while pi session was running.",
16598
16700
  retryable: false
16599
16701
  }
16600
16702
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/pi-extension",
3
- "version": "0.19.6",
3
+ "version": "0.20.1",
4
4
  "type": "module",
5
5
  "description": "MoltNet pi extension — sandboxed tool execution in Gondolin VMs with MoltNet identity and persistent memory",
6
6
  "license": "MIT",
@@ -31,8 +31,8 @@
31
31
  "@earendil-works/gondolin": "^0.9.1",
32
32
  "@opentelemetry/api": "^1.9.0",
33
33
  "@sinclair/typebox": "^0.34.0",
34
- "@themoltnet/agent-runtime": "0.18.5",
35
- "@themoltnet/sdk": "0.105.1"
34
+ "@themoltnet/agent-runtime": "0.19.1",
35
+ "@themoltnet/sdk": "0.106.0"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@earendil-works/pi-coding-agent": ">=0.74.0",