@themoltnet/node-red-contrib-core 0.3.2 → 0.4.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.
package/dist/nodes/src.js CHANGED
@@ -1831,6 +1831,28 @@ var uploadRuntimeSession = (options) => (options.client ?? client).put({
1831
1831
  }
1832
1832
  });
1833
1833
  /**
1834
+ * List recent team-scoped runtime slots for repair/sync.
1835
+ */
1836
+ var listRuntimeSlots = (options) => (options.client ?? client).get({
1837
+ security: [
1838
+ {
1839
+ scheme: "bearer",
1840
+ type: "http"
1841
+ },
1842
+ {
1843
+ name: "X-Moltnet-Session-Token",
1844
+ type: "apiKey"
1845
+ },
1846
+ {
1847
+ in: "cookie",
1848
+ name: "ory_kratos_session",
1849
+ type: "apiKey"
1850
+ }
1851
+ ],
1852
+ url: "/runtime-slots",
1853
+ ...options
1854
+ });
1855
+ /**
1834
1856
  * Upsert a team-scoped runtime slot for audit and continuation affinity lookup.
1835
1857
  */
1836
1858
  var beginRuntimeSlot = (options) => (options.client ?? client).post({
@@ -2249,6 +2271,55 @@ var claimTask = (options) => (options.client ?? client).post({
2249
2271
  }
2250
2272
  });
2251
2273
  /**
2274
+ * List task artifact metadata for the current team.
2275
+ */
2276
+ var listTaskArtifacts = (options) => (options.client ?? client).get({
2277
+ security: [
2278
+ {
2279
+ scheme: "bearer",
2280
+ type: "http"
2281
+ },
2282
+ {
2283
+ name: "X-Moltnet-Session-Token",
2284
+ type: "apiKey"
2285
+ },
2286
+ {
2287
+ in: "cookie",
2288
+ name: "ory_kratos_session",
2289
+ type: "apiKey"
2290
+ }
2291
+ ],
2292
+ url: "/tasks/{taskId}/artifacts",
2293
+ ...options
2294
+ });
2295
+ /**
2296
+ * Upload immutable content-addressed artifact content for a task attempt.
2297
+ */
2298
+ var uploadTaskArtifact = (options) => (options.client ?? client).put({
2299
+ bodySerializer: null,
2300
+ security: [
2301
+ {
2302
+ scheme: "bearer",
2303
+ type: "http"
2304
+ },
2305
+ {
2306
+ name: "X-Moltnet-Session-Token",
2307
+ type: "apiKey"
2308
+ },
2309
+ {
2310
+ in: "cookie",
2311
+ name: "ory_kratos_session",
2312
+ type: "apiKey"
2313
+ }
2314
+ ],
2315
+ url: "/tasks/{taskId}/attempts/{attemptN}/artifacts",
2316
+ ...options,
2317
+ headers: {
2318
+ "Content-Type": "application/octet-stream",
2319
+ ...options.headers
2320
+ }
2321
+ });
2322
+ /**
2252
2323
  * List teams the caller belongs to.
2253
2324
  */
2254
2325
  var listTeams = (options) => (options?.client ?? client).get({
@@ -5134,6 +5205,15 @@ function createRuntimeSlotsNamespace(context) {
5134
5205
  if (err instanceof MoltNetError && err.statusCode === 404) return null;
5135
5206
  throw err;
5136
5207
  }
5208
+ },
5209
+ async list(query, options) {
5210
+ const filteredQuery = Object.fromEntries(Object.entries(query).filter(([, value]) => value !== void 0));
5211
+ return unwrapResult(await listRuntimeSlots({
5212
+ auth,
5213
+ client,
5214
+ headers: requiredTeamHeaders(options),
5215
+ query: filteredQuery
5216
+ })).items;
5137
5217
  }
5138
5218
  };
5139
5219
  }
@@ -9483,6 +9563,32 @@ var RuntimeProfileAllowedWorkspaceModes = _Array_(RuntimeProfileWorkspaceMode, {
9483
9563
  maxItems: 3,
9484
9564
  uniqueItems: true
9485
9565
  });
9566
+ var RuntimeProfileThinkingLevelOptions = [
9567
+ Literal("off"),
9568
+ Literal("minimal"),
9569
+ Literal("low"),
9570
+ Literal("medium"),
9571
+ Literal("high"),
9572
+ Literal("xhigh")
9573
+ ];
9574
+ Union([...RuntimeProfileThinkingLevelOptions]);
9575
+ var RuntimeProfileNullableThinkingLevel = Union([...RuntimeProfileThinkingLevelOptions, Null()]);
9576
+ var RuntimeProfileNullableTemperature = Union([Null(), Number$1({
9577
+ minimum: 0,
9578
+ maximum: 2
9579
+ })]);
9580
+ var RuntimeProfileNullableTopP = Union([Null(), Number$1({
9581
+ minimum: 0,
9582
+ maximum: 1
9583
+ })]);
9584
+ var RuntimeProfileNullableTopK = Union([Integer({
9585
+ minimum: 1,
9586
+ maximum: 1e4
9587
+ }), Null()]);
9588
+ var RuntimeProfileNullableMaxOutputTokens = Union([Integer({
9589
+ minimum: 1,
9590
+ maximum: 1e6
9591
+ }), Null()]);
9486
9592
  var SandboxResumeCommandWhenSchema = _Object_({ workspaceMode: Optional(_Array_(Union([
9487
9593
  Literal("shared_mount"),
9488
9594
  Literal("dedicated_worktree"),
@@ -9607,6 +9713,11 @@ _Object_({
9607
9713
  minLength: 1,
9608
9714
  maxLength: 200
9609
9715
  }),
9716
+ thinkingLevel: RuntimeProfileNullableThinkingLevel,
9717
+ temperature: RuntimeProfileNullableTemperature,
9718
+ topP: RuntimeProfileNullableTopP,
9719
+ topK: RuntimeProfileNullableTopK,
9720
+ maxOutputTokens: RuntimeProfileNullableMaxOutputTokens,
9610
9721
  runtimeKind: Literal("gondolin_pi"),
9611
9722
  sandbox: RuntimeProfileSandbox,
9612
9723
  sessionStorageMode: Literal("local"),
@@ -9718,7 +9829,7 @@ var RuntimeWorkspace = _Object_({
9718
9829
  createdAtMs: Integer({ minimum: 0 }),
9719
9830
  lastUsedAtMs: Integer({ minimum: 0 })
9720
9831
  }, { $id: "RuntimeWorkspace" });
9721
- _Object_({
9832
+ _Object_({ items: _Array_(_Object_({
9722
9833
  slot: _Object_({
9723
9834
  id: String$1({ format: "uuid" }),
9724
9835
  teamId: String$1({ format: "uuid" }),
@@ -9751,7 +9862,7 @@ _Object_({
9751
9862
  expiresAtMs: Integer({ minimum: 0 })
9752
9863
  }, { $id: "RuntimeSlot" }),
9753
9864
  workspace: Union([RuntimeWorkspace, Null()])
9754
- }, { $id: "ResolvedRuntimeSlot" });
9865
+ }, { $id: "ResolvedRuntimeSlot" })) }, { $id: "RuntimeSlotListResponse" });
9755
9866
  _Object_({
9756
9867
  agentName: String$1({
9757
9868
  minLength: 1,
@@ -9812,6 +9923,21 @@ _Object_({
9812
9923
  $id: "FindLatestRuntimeSlotForAttemptQuery",
9813
9924
  additionalProperties: false
9814
9925
  });
9926
+ _Object_({
9927
+ agentName: Optional(String$1({
9928
+ minLength: 1,
9929
+ maxLength: 100
9930
+ })),
9931
+ runtimeProfileId: Optional(String$1({ format: "uuid" })),
9932
+ state: Optional(RuntimeSlotState),
9933
+ limit: Optional(Integer({
9934
+ minimum: 1,
9935
+ maximum: 200
9936
+ }))
9937
+ }, {
9938
+ $id: "ListRuntimeSlotsQuery",
9939
+ additionalProperties: false
9940
+ });
9815
9941
  //#endregion
9816
9942
  //#region ../tasks/src/success-criteria.ts
9817
9943
  /**
@@ -9959,6 +10085,97 @@ var VerificationRecord = _Object_({
9959
10085
  $id: "VerificationRecord",
9960
10086
  additionalProperties: false
9961
10087
  });
10088
+ _Object_({
10089
+ artifacts: _Array_(_Object_({
10090
+ id: String$1({ format: "uuid" }),
10091
+ teamId: String$1({ format: "uuid" }),
10092
+ taskId: String$1({ format: "uuid" }),
10093
+ attemptN: Integer({ minimum: 1 }),
10094
+ kind: String$1({
10095
+ minLength: 1,
10096
+ maxLength: 100
10097
+ }),
10098
+ title: String$1({
10099
+ minLength: 1,
10100
+ maxLength: 255
10101
+ }),
10102
+ contentType: String$1({
10103
+ minLength: 1,
10104
+ maxLength: 200
10105
+ }),
10106
+ contentEncoding: Union([String$1({
10107
+ minLength: 1,
10108
+ maxLength: 100
10109
+ }), Null()]),
10110
+ sizeBytes: Integer({ minimum: 0 }),
10111
+ cid: String$1({
10112
+ minLength: 1,
10113
+ maxLength: 100
10114
+ }),
10115
+ createdByAgentId: String$1({ format: "uuid" }),
10116
+ expiresAt: Union([String$1({ format: "date-time" }), Null()]),
10117
+ createdAt: String$1({ format: "date-time" })
10118
+ }, { $id: "TaskArtifact" })),
10119
+ nextCursor: Union([String$1({ minLength: 1 }), Null()])
10120
+ }, { $id: "TaskArtifactList" });
10121
+ _Object_({
10122
+ limit: Optional(Integer({
10123
+ minimum: 1,
10124
+ maximum: 100
10125
+ })),
10126
+ cursor: Optional(String$1({ minLength: 1 }))
10127
+ }, {
10128
+ $id: "ListTaskArtifactsQuery",
10129
+ additionalProperties: false
10130
+ });
10131
+ _Object_({
10132
+ kind: String$1({
10133
+ minLength: 1,
10134
+ maxLength: 100
10135
+ }),
10136
+ title: String$1({
10137
+ minLength: 1,
10138
+ maxLength: 255
10139
+ }),
10140
+ contentType: Optional(String$1({
10141
+ minLength: 1,
10142
+ maxLength: 200
10143
+ })),
10144
+ contentEncoding: Optional(String$1({
10145
+ minLength: 1,
10146
+ maxLength: 100
10147
+ }))
10148
+ }, {
10149
+ $id: "UploadTaskArtifactQuery",
10150
+ additionalProperties: false
10151
+ });
10152
+ String$1({
10153
+ $id: "TaskArtifactContent",
10154
+ description: "Task artifact content stream.",
10155
+ format: "binary"
10156
+ });
10157
+ _Object_({ taskId: String$1({ format: "uuid" }) }, {
10158
+ $id: "TaskArtifactTaskParams",
10159
+ additionalProperties: false
10160
+ });
10161
+ _Object_({
10162
+ taskId: String$1({ format: "uuid" }),
10163
+ attemptN: Integer({ minimum: 1 })
10164
+ }, {
10165
+ $id: "TaskArtifactAttemptParams",
10166
+ additionalProperties: false
10167
+ });
10168
+ _Object_({
10169
+ taskId: String$1({ format: "uuid" }),
10170
+ attemptN: Integer({ minimum: 1 }),
10171
+ cid: String$1({
10172
+ minLength: 1,
10173
+ maxLength: 100
10174
+ })
10175
+ }, {
10176
+ $id: "TaskArtifactContentParams",
10177
+ additionalProperties: false
10178
+ });
9962
10179
  new TextEncoder();
9963
10180
  new TextDecoder();
9964
10181
  //#endregion
@@ -10218,6 +10435,10 @@ var FreeformArtifact = _Object_({
10218
10435
  description: Optional(String$1({ minLength: 1 })),
10219
10436
  url: Optional(String$1({ minLength: 1 })),
10220
10437
  path: Optional(String$1({ minLength: 1 })),
10438
+ cid: Optional(String$1({ minLength: 1 })),
10439
+ contentType: Optional(String$1({ minLength: 1 })),
10440
+ contentEncoding: Optional(String$1({ minLength: 1 })),
10441
+ sizeBytes: Optional(Integer({ minimum: 0 })),
10221
10442
  body: Optional(String$1({ maxLength: 65536 }))
10222
10443
  }, {
10223
10444
  $id: "FreeformArtifact",
@@ -10225,6 +10446,7 @@ var FreeformArtifact = _Object_({
10225
10446
  });
10226
10447
  var FreeformOutput = _Object_({
10227
10448
  summary: String$1({ minLength: 1 }),
10449
+ branch: Optional(String$1({ minLength: 1 })),
10228
10450
  artifacts: Optional(_Array_(FreeformArtifact, { maxItems: 20 })),
10229
10451
  proposedTaskType: Optional(FreeformTaskTypeProposal),
10230
10452
  diaryEntryIds: Optional(_Array_(String$1({ format: "uuid" }))),
@@ -10237,7 +10459,7 @@ var FreeformOutput = _Object_({
10237
10459
  * Server-side preflight for `freeform` task-create. Runs after the
10238
10460
  * sync TypeBox check passes and only kicks in when
10239
10461
  * `input.continueFrom` is set — i.e. the proposer is asking to
10240
- * resume a prior freeform attempt's warm slot (#1287).
10462
+ * continue from a prior freeform attempt (#1287).
10241
10463
  *
10242
10464
  * Failure modes, in evaluation order:
10243
10465
  * 1. `freeform.sourceTaskNotFound` — source task id does not resolve
@@ -10245,23 +10467,17 @@ var FreeformOutput = _Object_({
10245
10467
  * 2. `freeform.sourceTaskTypeNotSupported` — source isn't `freeform`.
10246
10468
  * v1 only supports freeform → freeform continuation.
10247
10469
  * 3. `freeform.sourceAttemptNotCompleted` — named attempt is missing
10248
- * or not in `completed` state; warm continuation only makes sense
10470
+ * or not in `completed` state; continuation only makes sense
10249
10471
  * once the parent has produced a terminal output.
10250
10472
  * 4. `freeform.executionWorkspaceNotInheritable` — caller set
10251
10473
  * `execution.workspace` together with `continueFrom`. Workspace
10252
- * mode for a continuation is inherited from the parent slot
10253
- * (`maybeAttachWarmSlotContext` forces `dedicated_worktree` +
10254
- * the parent's worktreeBranch), so any caller-supplied override
10255
- * is silently dropped at the daemon plan stage. Reject explicitly
10256
- * so misconfiguration surfaces at create time.
10257
- * 5. `freeform.sourceNotResumeEligible` — `daemonState` is null or
10258
- * `slotResumableUntil` is null. Older completions (pre-#1287) and
10259
- * daemons that opt out fall here.
10260
- * 6. `freeform.sourceResumeExpired` — `slotResumableUntil` is in the
10261
- * past; the warm slot's TTL has elapsed and no daemon is
10262
- * guaranteed to still hold it.
10474
+ * mode for a continuation is derived by the daemon from parent runtime
10475
+ * context (local slot first, durable session + source attempt branch
10476
+ * second), so any caller-supplied override is silently dropped at the
10477
+ * daemon plan stage. Reject explicitly so misconfiguration surfaces at
10478
+ * create time.
10263
10479
  *
10264
- * Returns on the first failure (no "report all six") — the checks
10480
+ * Returns on the first failure — the checks
10265
10481
  * are sequential preconditions, later ones presume earlier ones hold.
10266
10482
  */
10267
10483
  async function validateFreeformInputAsync(input, ctx) {
@@ -10280,7 +10496,7 @@ async function validateFreeformInputAsync(input, ctx) {
10280
10496
  }];
10281
10497
  if (input.execution?.workspace) return [{
10282
10498
  field: "input/execution/workspace",
10283
- message: "execution.workspace is inherited from the parent slot when continueFrom is set; omit it",
10499
+ message: "execution.workspace is derived from parent runtime context when continueFrom is set; omit it",
10284
10500
  code: "freeform.executionWorkspaceNotInheritable"
10285
10501
  }];
10286
10502
  if (ctx.deferReadinessChecks) return [];
@@ -10290,17 +10506,6 @@ async function validateFreeformInputAsync(input, ctx) {
10290
10506
  message: `Source attempt ${cf.attemptN} on task ${cf.taskId} is not in 'completed' state`,
10291
10507
  code: "freeform.sourceAttemptNotCompleted"
10292
10508
  }];
10293
- if (!attempt.daemonState || attempt.daemonState.slotResumableUntil === null) return [{
10294
- field: "input/continueFrom",
10295
- message: "Source attempt did not report continuation eligibility (older completion or daemon opted out)",
10296
- code: "freeform.sourceNotResumeEligible"
10297
- }];
10298
- const expiresAt = new Date(attempt.daemonState.slotResumableUntil).getTime();
10299
- if (Number.isNaN(expiresAt) || expiresAt <= Date.now()) return [{
10300
- field: "input/continueFrom",
10301
- message: `Source attempt's warm slot expired at ${attempt.daemonState.slotResumableUntil} (reported at ${attempt.daemonState.reportedAt})`,
10302
- code: "freeform.sourceResumeExpired"
10303
- }];
10304
10509
  return [];
10305
10510
  }
10306
10511
  //#endregion
@@ -13635,11 +13840,12 @@ var MAX_CLAIM_CONDITION_STATUSES = 8;
13635
13840
  /**
13636
13841
  * Daemon-asserted runtime state stamped onto a `TaskAttemptSummary` at
13637
13842
  * attempt-completion time. The server persists this block verbatim and
13638
- * reads `slotResumableUntil` for `tasks_continue` create-time
13639
- * eligibility; the daemon-side claim-affinity filter is the runtime
13640
- * truth. The block carries its own `reportedAt` so consumers can reason
13641
- * about staleness without reading documentation. All daemon-asserted
13642
- * state lives here — top-level attempt fields stay server-authoritative.
13843
+ * exposes `slotResumableUntil` as a legacy/local warm-slot hint; task
13844
+ * continuation eligibility is based on the completed source attempt and
13845
+ * daemon-side claim-affinity/runtime-session recovery. The block carries
13846
+ * its own `reportedAt` so consumers can reason about staleness without
13847
+ * reading documentation. All daemon-asserted state lives here
13848
+ * top-level attempt fields stay server-authoritative.
13643
13849
  *
13644
13850
  * Adding new fields requires explicit design review (intentional
13645
13851
  * boundary; see docs/superpowers/specs/2026-06-04-tasks-continue-design.md).
@@ -13703,7 +13909,23 @@ var TaskRef = _Object_({
13703
13909
  url: Optional(String$1()),
13704
13910
  commit_sha: Optional(String$1()),
13705
13911
  snapshot_cid: Optional(Cid)
13706
- }))
13912
+ })),
13913
+ artifact: Optional(_Object_({
13914
+ cid: Cid,
13915
+ attemptN: Integer({ minimum: 1 }),
13916
+ kind: Optional(String$1({
13917
+ minLength: 1,
13918
+ maxLength: 100
13919
+ })),
13920
+ title: Optional(String$1({
13921
+ minLength: 1,
13922
+ maxLength: 255
13923
+ })),
13924
+ contentType: Optional(String$1({
13925
+ minLength: 1,
13926
+ maxLength: 200
13927
+ }))
13928
+ }, { additionalProperties: false }))
13707
13929
  }, {
13708
13930
  $id: "TaskRef",
13709
13931
  additionalProperties: false
@@ -14048,6 +14270,7 @@ var TaskBuilder = class {
14048
14270
  message: "reference is missing required outputCid"
14049
14271
  }]);
14050
14272
  ref = {
14273
+ ...s,
14051
14274
  taskId: s.taskId ?? null,
14052
14275
  outputCid: s.outputCid,
14053
14276
  role
@@ -14057,6 +14280,60 @@ var TaskBuilder = class {
14057
14280
  return this;
14058
14281
  }
14059
14282
  /**
14283
+ * Add a reference to a persistent task artifact while retaining the accepted
14284
+ * output CID as the provenance anchor.
14285
+ *
14286
+ * @param source - A result reader, raw artifact reference, or `TaskRef`.
14287
+ * @param role - The role the referenced artifact plays.
14288
+ * @returns This builder, for chaining.
14289
+ * @throws {TaskBuildError} when output or artifact CID is missing.
14290
+ */
14291
+ artifactReference(source, role) {
14292
+ let ref;
14293
+ if ("artifactRef" in source && typeof source.artifactRef === "function") ref = source.artifactRef(role);
14294
+ else if ("artifact" in source && source.artifact?.cid) {
14295
+ if (typeof source.artifact.attemptN !== "number" || !Number.isInteger(source.artifact.attemptN) || source.artifact.attemptN < 1) throw new TaskBuildError([{
14296
+ field: "references/artifact/attemptN",
14297
+ message: "artifact reference is missing required attemptN"
14298
+ }]);
14299
+ ref = {
14300
+ ...source,
14301
+ role
14302
+ };
14303
+ } else {
14304
+ const s = source;
14305
+ const errors = [];
14306
+ if (!s.outputCid) errors.push({
14307
+ field: "references/outputCid",
14308
+ message: "reference is missing required outputCid"
14309
+ });
14310
+ if (!s.artifactCid) errors.push({
14311
+ field: "references/artifact/cid",
14312
+ message: "artifact reference is missing required cid"
14313
+ });
14314
+ if (typeof s.attemptN !== "number" || !Number.isInteger(s.attemptN) || s.attemptN < 1) errors.push({
14315
+ field: "references/artifact/attemptN",
14316
+ message: "artifact reference is missing required attemptN"
14317
+ });
14318
+ if (errors.length > 0) throw new TaskBuildError(errors);
14319
+ const attemptN = s.attemptN;
14320
+ ref = {
14321
+ taskId: s.taskId ?? null,
14322
+ outputCid: s.outputCid,
14323
+ role,
14324
+ artifact: {
14325
+ cid: s.artifactCid,
14326
+ attemptN,
14327
+ ...s.kind ? { kind: s.kind } : {},
14328
+ ...s.title ? { title: s.title } : {},
14329
+ ...s.contentType ? { contentType: s.contentType } : {}
14330
+ }
14331
+ };
14332
+ }
14333
+ this.refs.push(ref);
14334
+ return this;
14335
+ }
14336
+ /**
14060
14337
  * Set the owning team (required by the wire schema).
14061
14338
  *
14062
14339
  * @param teamId - Team UUID.
@@ -14434,6 +14711,34 @@ var TaskResultReader = class {
14434
14711
  role
14435
14712
  };
14436
14713
  }
14714
+ /**
14715
+ * Build a `TaskRef` that anchors a downstream task to this accepted output
14716
+ * and points at one persistent task artifact by CID.
14717
+ *
14718
+ * @param filter - Artifact object or a filter resolved against output artifacts.
14719
+ * @param role - The role this artifact plays in the downstream task.
14720
+ * @returns A `TaskRef` with `artifact.cid` populated.
14721
+ * @throws {TaskResultError} if no matching artifact has a CID.
14722
+ */
14723
+ artifactRef(filter, role) {
14724
+ const artifact = typeof filter === "object" && "cid" in filter && "kind" in filter ? filter : this.artifact(filter);
14725
+ if (!artifact?.cid) throw new TaskResultError([{
14726
+ field: "artifacts/cid",
14727
+ message: "no matching artifact with a cid"
14728
+ }]);
14729
+ return {
14730
+ taskId: this.taskId,
14731
+ outputCid: this.outputCid,
14732
+ role,
14733
+ artifact: {
14734
+ cid: artifact.cid,
14735
+ attemptN: this.accepted.attemptN,
14736
+ kind: artifact.kind,
14737
+ title: artifact.title,
14738
+ ...artifact.contentType ? { contentType: artifact.contentType } : {}
14739
+ }
14740
+ };
14741
+ }
14437
14742
  };
14438
14743
  /**
14439
14744
  * Validate and construct a {@link TaskResultReader} from a task and its
@@ -14458,6 +14763,63 @@ function createTasksNamespace(context) {
14458
14763
  auth
14459
14764
  }));
14460
14765
  },
14766
+ artifacts: {
14767
+ async upload(path, body, query, options) {
14768
+ return unwrapResult(await uploadTaskArtifact({
14769
+ auth,
14770
+ body,
14771
+ client,
14772
+ duplex: "half",
14773
+ headers: {
14774
+ ...requiredTeamHeaders(options),
14775
+ "content-type": "application/octet-stream"
14776
+ },
14777
+ path,
14778
+ query
14779
+ }));
14780
+ },
14781
+ async list(taskId, options, query) {
14782
+ return unwrapResult(await listTaskArtifacts({
14783
+ client,
14784
+ auth,
14785
+ headers: requiredTeamHeaders(options),
14786
+ path: { taskId },
14787
+ query
14788
+ })).artifacts;
14789
+ },
14790
+ async listPage(taskId, query, options) {
14791
+ return unwrapResult(await listTaskArtifacts({
14792
+ client,
14793
+ auth,
14794
+ headers: requiredTeamHeaders(options),
14795
+ path: { taskId },
14796
+ query
14797
+ }));
14798
+ },
14799
+ async download(path, options) {
14800
+ const result = await client.request({
14801
+ auth,
14802
+ headers: requiredTeamHeaders(options),
14803
+ method: "GET",
14804
+ parseAs: "stream",
14805
+ path,
14806
+ security: [{
14807
+ scheme: "bearer",
14808
+ type: "http"
14809
+ }],
14810
+ url: "/tasks/{taskId}/attempts/{attemptN}/artifacts/{cid}/content"
14811
+ });
14812
+ const normalizedStream = normalizeDownloadStream(unwrapResult(result));
14813
+ if (normalizedStream) return {
14814
+ artifactId: header(result.response, "x-moltnet-task-artifact-id"),
14815
+ cid: header(result.response, "x-moltnet-task-artifact-cid"),
14816
+ contentEncoding: header(result.response, "x-moltnet-task-artifact-content-encoding"),
14817
+ contentType: header(result.response, "x-moltnet-task-artifact-content-type"),
14818
+ stream: normalizedStream
14819
+ };
14820
+ throw new MoltNetError("Unexpected task artifact download response stream", { code: "INVALID_RESPONSE" });
14821
+ }
14822
+ },
14461
14823
  async list(query, options) {
14462
14824
  return unwrapResult(await listTasks({
14463
14825
  client,
@@ -14626,6 +14988,33 @@ function createTasksNamespace(context) {
14626
14988
  }
14627
14989
  };
14628
14990
  }
14991
+ function header(response, name) {
14992
+ const value = response?.headers.get(name) ?? null;
14993
+ return value === "" ? null : value;
14994
+ }
14995
+ function normalizeDownloadStream(stream) {
14996
+ if (isAsyncIterable(stream)) return stream;
14997
+ if (isReadableStream(stream)) return readableStreamToAsyncIterable(stream);
14998
+ return null;
14999
+ }
15000
+ function isAsyncIterable(value) {
15001
+ return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
15002
+ }
15003
+ function isReadableStream(value) {
15004
+ return typeof value === "object" && value !== null && "getReader" in value && typeof value.getReader === "function";
15005
+ }
15006
+ async function* readableStreamToAsyncIterable(stream) {
15007
+ const reader = stream.getReader();
15008
+ try {
15009
+ while (true) {
15010
+ const result = await reader.read();
15011
+ if (result.done) return;
15012
+ yield result.value;
15013
+ }
15014
+ } finally {
15015
+ reader.releaseLock();
15016
+ }
15017
+ }
14629
15018
  //#endregion
14630
15019
  //#region ../sdk/src/namespaces/teams.ts
14631
15020
  function createTeamsNamespace(context) {
@@ -19,16 +19,13 @@ var init = (RED) => {
19
19
  } : def.artifactKind : void 0;
20
20
  let artifactBody;
21
21
  const artifact = filter ? reader.artifact(filter) : void 0;
22
- if (filter && artifact && typeof artifact.body === "string") try {
23
- artifactBody = JSON.parse(artifact.body);
24
- } catch {
25
- artifactBody = void 0;
26
- }
22
+ if (filter && artifact && typeof artifact.body === "string") artifactBody = reader.artifactBody(filter);
27
23
  const out = RED.util.cloneMessage(msg);
28
24
  out.payload = reader.output;
29
25
  out.result = {
30
26
  summary: reader.summary,
31
27
  outputRef: reader.outputRef(role),
28
+ artifactRef: artifact?.cid !== void 0 ? reader.artifactRef(artifact, role) : void 0,
32
29
  artifact,
33
30
  artifactBody,
34
31
  accepted: reader.accepted,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/node-red-contrib-core",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "Node-RED nodes for the MoltNet API",
6
6
  "keywords": [
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "main": "dist/nodes/agent.js",
40
40
  "dependencies": {
41
- "@themoltnet/sdk": "0.113.1"
41
+ "@themoltnet/sdk": "0.115.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^22.19.0",