@looplia/looplia-cli 0.7.3 → 0.7.5

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.
@@ -4,10 +4,11 @@ import {
4
4
  pathExists
5
5
  } from "./chunk-VRBGWKZ6.js";
6
6
  import {
7
+ __export,
7
8
  init_esm_shims
8
9
  } from "./chunk-Y55L47HC.js";
9
10
 
10
- // ../../packages/provider/dist/chunk-DRG2TRPF.js
11
+ // ../../packages/provider/dist/chunk-5BLYIGE4.js
11
12
  init_esm_shims();
12
13
  import { createHash } from "crypto";
13
14
  import {
@@ -29,7 +30,6 @@ import { promisify } from "util";
29
30
  var execAsync = promisify(exec);
30
31
  var CORE_SKILLS = [
31
32
  "workflow-executor",
32
- "workflow-executor-inline",
33
33
  "workflow-validator",
34
34
  "registry-loader"
35
35
  ];
@@ -293,116 +293,123 @@ async function getPluginPaths() {
293
293
  return await getProdPluginPaths();
294
294
  }
295
295
 
296
- // ../../packages/provider/dist/chunk-3N4TZG2S.js
296
+ // ../../packages/core/dist/index.js
297
297
  init_esm_shims();
298
- import { execSync } from "child_process";
299
- import { existsSync as existsSync3 } from "fs";
300
- import { createRequire } from "module";
301
- import { homedir as homedir3 } from "os";
302
- import { dirname as dirname3, join as join4 } from "path";
303
- import {
304
- chmod,
305
- mkdir as mkdir3,
306
- readFile as readFile2,
307
- rename,
308
- rm as rm2,
309
- writeFile as writeFile2
310
- } from "fs/promises";
311
- import { homedir as homedir22 } from "os";
312
- import { join as join23 } from "path";
313
-
314
- // ../../node_modules/zod-to-json-schema/dist/esm/index.js
315
- init_esm_shims();
316
-
317
- // ../../node_modules/zod-to-json-schema/dist/esm/Options.js
318
- init_esm_shims();
319
- var ignoreOverride = /* @__PURE__ */ Symbol("Let zodToJsonSchema decide on which parser to use");
320
- var defaultOptions = {
321
- name: void 0,
322
- $refStrategy: "root",
323
- basePath: ["#"],
324
- effectStrategy: "input",
325
- pipeStrategy: "all",
326
- dateStrategy: "format:date-time",
327
- mapStrategy: "entries",
328
- removeAdditionalStrategy: "passthrough",
329
- allowedAdditionalProperties: true,
330
- rejectedAdditionalProperties: false,
331
- definitionPath: "definitions",
332
- target: "jsonSchema7",
333
- strictUnions: false,
334
- definitions: {},
335
- errorMessages: false,
336
- markdownDescription: false,
337
- patternStrategy: "escape",
338
- applyRegexFlags: false,
339
- emailStrategy: "format:email",
340
- base64Strategy: "contentEncoding:base64",
341
- nameStrategy: "ref",
342
- openAiAnyTypeName: "OpenAiAnyType"
343
- };
344
- var getDefaultOptions = (options) => typeof options === "string" ? {
345
- ...defaultOptions,
346
- name: options
347
- } : {
348
- ...defaultOptions,
349
- ...options
350
- };
351
-
352
- // ../../node_modules/zod-to-json-schema/dist/esm/Refs.js
353
- init_esm_shims();
354
- var getRefs = (options) => {
355
- const _options = getDefaultOptions(options);
356
- const currentPath = _options.name !== void 0 ? [..._options.basePath, _options.definitionPath, _options.name] : _options.basePath;
357
- return {
358
- ..._options,
359
- flags: { hasReferencedOpenAiAnyType: false },
360
- currentPath,
361
- propertyPath: void 0,
362
- seen: new Map(Object.entries(_options.definitions).map(([name, def]) => [
363
- def._def,
364
- {
365
- def: def._def,
366
- path: [..._options.basePath, _options.definitionPath, name],
367
- // Resolution of references will be forced even though seen, so it's ok that the schema is undefined here for now.
368
- jsonSchema: void 0
369
- }
370
- ]))
371
- };
372
- };
373
-
374
- // ../../node_modules/zod-to-json-schema/dist/esm/errorMessages.js
375
- init_esm_shims();
376
- function addErrorMessage(res, key, errorMessage, refs) {
377
- if (!refs?.errorMessages)
378
- return;
379
- if (errorMessage) {
380
- res.errorMessage = {
381
- ...res.errorMessage,
382
- [key]: errorMessage
383
- };
384
- }
385
- }
386
- function setResponseValueAndErrors(res, key, value, errorMessage, refs) {
387
- res[key] = value;
388
- addErrorMessage(res, key, errorMessage, refs);
389
- }
390
-
391
- // ../../node_modules/zod-to-json-schema/dist/esm/getRelativePath.js
392
- init_esm_shims();
393
- var getRelativePath = (pathA, pathB) => {
394
- let i = 0;
395
- for (; i < pathA.length && i < pathB.length; i++) {
396
- if (pathA[i] !== pathB[i])
397
- break;
398
- }
399
- return [(pathA.length - i).toString(), ...pathB.slice(i)].join("/");
400
- };
401
298
 
402
- // ../../node_modules/zod-to-json-schema/dist/esm/parseDef.js
299
+ // ../../node_modules/zod/index.js
403
300
  init_esm_shims();
404
301
 
405
- // ../../node_modules/zod-to-json-schema/dist/esm/selectParser.js
302
+ // ../../node_modules/zod/v3/external.js
303
+ var external_exports = {};
304
+ __export(external_exports, {
305
+ BRAND: () => BRAND,
306
+ DIRTY: () => DIRTY,
307
+ EMPTY_PATH: () => EMPTY_PATH,
308
+ INVALID: () => INVALID,
309
+ NEVER: () => NEVER,
310
+ OK: () => OK,
311
+ ParseStatus: () => ParseStatus,
312
+ Schema: () => ZodType,
313
+ ZodAny: () => ZodAny,
314
+ ZodArray: () => ZodArray,
315
+ ZodBigInt: () => ZodBigInt,
316
+ ZodBoolean: () => ZodBoolean,
317
+ ZodBranded: () => ZodBranded,
318
+ ZodCatch: () => ZodCatch,
319
+ ZodDate: () => ZodDate,
320
+ ZodDefault: () => ZodDefault,
321
+ ZodDiscriminatedUnion: () => ZodDiscriminatedUnion,
322
+ ZodEffects: () => ZodEffects,
323
+ ZodEnum: () => ZodEnum,
324
+ ZodError: () => ZodError,
325
+ ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind,
326
+ ZodFunction: () => ZodFunction,
327
+ ZodIntersection: () => ZodIntersection,
328
+ ZodIssueCode: () => ZodIssueCode,
329
+ ZodLazy: () => ZodLazy,
330
+ ZodLiteral: () => ZodLiteral,
331
+ ZodMap: () => ZodMap,
332
+ ZodNaN: () => ZodNaN,
333
+ ZodNativeEnum: () => ZodNativeEnum,
334
+ ZodNever: () => ZodNever,
335
+ ZodNull: () => ZodNull,
336
+ ZodNullable: () => ZodNullable,
337
+ ZodNumber: () => ZodNumber,
338
+ ZodObject: () => ZodObject,
339
+ ZodOptional: () => ZodOptional,
340
+ ZodParsedType: () => ZodParsedType,
341
+ ZodPipeline: () => ZodPipeline,
342
+ ZodPromise: () => ZodPromise,
343
+ ZodReadonly: () => ZodReadonly,
344
+ ZodRecord: () => ZodRecord,
345
+ ZodSchema: () => ZodType,
346
+ ZodSet: () => ZodSet,
347
+ ZodString: () => ZodString,
348
+ ZodSymbol: () => ZodSymbol,
349
+ ZodTransformer: () => ZodEffects,
350
+ ZodTuple: () => ZodTuple,
351
+ ZodType: () => ZodType,
352
+ ZodUndefined: () => ZodUndefined,
353
+ ZodUnion: () => ZodUnion,
354
+ ZodUnknown: () => ZodUnknown,
355
+ ZodVoid: () => ZodVoid,
356
+ addIssueToContext: () => addIssueToContext,
357
+ any: () => anyType,
358
+ array: () => arrayType,
359
+ bigint: () => bigIntType,
360
+ boolean: () => booleanType,
361
+ coerce: () => coerce,
362
+ custom: () => custom,
363
+ date: () => dateType,
364
+ datetimeRegex: () => datetimeRegex,
365
+ defaultErrorMap: () => en_default,
366
+ discriminatedUnion: () => discriminatedUnionType,
367
+ effect: () => effectsType,
368
+ enum: () => enumType,
369
+ function: () => functionType,
370
+ getErrorMap: () => getErrorMap,
371
+ getParsedType: () => getParsedType,
372
+ instanceof: () => instanceOfType,
373
+ intersection: () => intersectionType,
374
+ isAborted: () => isAborted,
375
+ isAsync: () => isAsync,
376
+ isDirty: () => isDirty,
377
+ isValid: () => isValid,
378
+ late: () => late,
379
+ lazy: () => lazyType,
380
+ literal: () => literalType,
381
+ makeIssue: () => makeIssue,
382
+ map: () => mapType,
383
+ nan: () => nanType,
384
+ nativeEnum: () => nativeEnumType,
385
+ never: () => neverType,
386
+ null: () => nullType,
387
+ nullable: () => nullableType,
388
+ number: () => numberType,
389
+ object: () => objectType,
390
+ objectUtil: () => objectUtil,
391
+ oboolean: () => oboolean,
392
+ onumber: () => onumber,
393
+ optional: () => optionalType,
394
+ ostring: () => ostring,
395
+ pipeline: () => pipelineType,
396
+ preprocess: () => preprocessType,
397
+ promise: () => promiseType,
398
+ quotelessJson: () => quotelessJson,
399
+ record: () => recordType,
400
+ set: () => setType,
401
+ setErrorMap: () => setErrorMap,
402
+ strictObject: () => strictObjectType,
403
+ string: () => stringType,
404
+ symbol: () => symbolType,
405
+ transformer: () => effectsType,
406
+ tuple: () => tupleType,
407
+ undefined: () => undefinedType,
408
+ union: () => unionType,
409
+ unknown: () => unknownType,
410
+ util: () => util,
411
+ void: () => voidType
412
+ });
406
413
  init_esm_shims();
407
414
 
408
415
  // ../../node_modules/zod/v3/errors.js
@@ -4349,112 +4356,904 @@ var coerce = {
4349
4356
  };
4350
4357
  var NEVER = INVALID;
4351
4358
 
4352
- // ../../node_modules/zod-to-json-schema/dist/esm/parsers/any.js
4353
- init_esm_shims();
4354
- function parseAnyDef(refs) {
4355
- if (refs.target !== "openAi") {
4356
- return {};
4359
+ // ../../packages/core/dist/index.js
4360
+ var commands = /* @__PURE__ */ new Map();
4361
+ function registerCommand(definition) {
4362
+ if (commands.has(definition.name)) {
4363
+ throw new Error(`Command "${definition.name}" is already registered`);
4357
4364
  }
4358
- const anyDefinitionPath = [
4359
- ...refs.basePath,
4360
- refs.definitionPath,
4361
- refs.openAiAnyTypeName
4362
- ];
4363
- refs.flags.hasReferencedOpenAiAnyType = true;
4364
- return {
4365
- $ref: refs.$refStrategy === "relative" ? getRelativePath(anyDefinitionPath, refs.currentPath) : anyDefinitionPath.join("/")
4366
- };
4365
+ commands.set(definition.name, definition);
4367
4366
  }
4367
+ function buildWorkflowPrompt(ctx) {
4368
+ if (!(ctx.workflowName && ctx.workflowDefinition)) {
4369
+ throw new Error(
4370
+ "Workflow context is required (workflowName, workflowDefinition)"
4371
+ );
4372
+ }
4373
+ return `Task: Execute workflow "${ctx.workflowName}" for session: contentItem/${ctx.contentId}
4368
4374
 
4369
- // ../../node_modules/zod-to-json-schema/dist/esm/parsers/array.js
4370
- init_esm_shims();
4371
- function parseArrayDef(def, refs) {
4372
- const res = {
4373
- type: "array"
4375
+ ## Workflow Definition
4376
+
4377
+ \`\`\`yaml
4378
+ ${ctx.workflowDefinition}
4379
+ \`\`\`
4380
+
4381
+ ${ctx.workflowInstructions ? `## Custom Instructions
4382
+
4383
+ ${ctx.workflowInstructions}
4384
+ ` : ""}
4385
+ ## Execution Protocol
4386
+
4387
+ ### Step 1: Read Validation State
4388
+ Read \`contentItem/${ctx.contentId}/validation.json\` to understand:
4389
+ - What outputs are required
4390
+ - Validation criteria for each output
4391
+ - Which outputs have already passed validation
4392
+
4393
+ If validation.json is missing, the workflow hasn't started properly.
4394
+
4395
+ ### Step 2: Execute Outputs (Dependency Order)
4396
+
4397
+ For each output in the workflow (following dependency order):
4398
+
4399
+ 1. **Check completion**:
4400
+ - If artifact exists AND validated=true in validation.json \u2192 skip
4401
+
4402
+ 2. **If incomplete - use Task tool with custom subagent_type**:
4403
+
4404
+ Use the **Task** tool to invoke the agent specified in the workflow definition.
4405
+ The subagent_type MUST match the \`agent\` field exactly from the workflow YAML.
4406
+
4407
+ Example for an output with \`agent: content-analyzer\`:
4408
+ \`\`\`json
4409
+ {
4410
+ "name": "Task",
4411
+ "input": {
4412
+ "subagent_type": "content-analyzer",
4413
+ "description": "Generate summary artifact",
4414
+ "prompt": "Analyze content at contentItem/${ctx.contentId}/content.md and write summary.json"
4415
+ }
4416
+ }
4417
+ \`\`\`
4418
+
4419
+ The subagent will:
4420
+ - Auto-load its configured skills (from .claude/agents/*.md)
4421
+ - Write artifact to \`contentItem/${ctx.contentId}/{artifact}\`
4422
+
4423
+ 3. **After artifact written - use Skill tool for validation**:
4424
+
4425
+ Use the **Skill** tool to invoke workflow-validator:
4426
+ \`\`\`json
4427
+ {
4428
+ "name": "Skill",
4429
+ "input": { "skill": "workflow-validator" }
4430
+ }
4431
+ \`\`\`
4432
+
4433
+ Then follow the skill's instructions to validate the artifact against criteria.
4434
+
4435
+ 4. **Handle validation result**:
4436
+ - If passed: Update validation.json \u2192 set \`outputs.{name}.validated = true\`
4437
+ - If failed: Review failed checks, retry subagent with specific feedback
4438
+
4439
+ ### Step 3: Return Final Output
4440
+
4441
+ When the output marked \`final: true\` passes validation:
4442
+ 1. Read the final artifact JSON
4443
+ 2. Return it as structured output
4444
+
4445
+ ## Workspace Structure
4446
+
4447
+ - **Content**: \`contentItem/${ctx.contentId}/content.md\`
4448
+ - **Validation**: \`contentItem/${ctx.contentId}/validation.json\`
4449
+ - **Artifacts**: \`contentItem/${ctx.contentId}/*.json\`
4450
+ - **Workflow**: \`workflows/${ctx.workflowName}.md\`
4451
+ - **Agents**: \`.claude/agents/*.md\`
4452
+ - **Skills**: \`.claude/skills/*/SKILL.md\`
4453
+
4454
+ ## Important Rules
4455
+
4456
+ - **Validate after each step** - Never skip validation
4457
+ - **Update validation.json** - Mark outputs validated when passed
4458
+ - **Follow dependencies** - Complete required outputs before dependent ones
4459
+ - **Retry on failure** - Give subagent specific feedback on validation failures
4460
+ - **Return structured JSON** - Final output must match expected schema`;
4461
+ }
4462
+ var workflowCommand = {
4463
+ name: "workflow",
4464
+ promptTemplate: buildWorkflowPrompt,
4465
+ // Generic object schema - allows any JSON object structure
4466
+ // Must be an object schema (not z.unknown()) to produce valid JSON schema with "type" field
4467
+ outputSchema: external_exports.record(external_exports.string(), external_exports.unknown())
4468
+ };
4469
+ registerCommand(workflowCommand);
4470
+ var RUN_PATTERN = /^agents\/([a-z][a-z0-9-]*)$/;
4471
+ function isValidRunFormat(run) {
4472
+ return RUN_PATTERN.test(run);
4473
+ }
4474
+ var INPUTLESS_CAPABLE_SKILLS = ["browser-research"];
4475
+ var ARRAY_PATTERN = /^\[(.*)\]$/;
4476
+ var INTEGER_PATTERN = /^\d+$/;
4477
+ var NON_SPACE_PATTERN = /\S/;
4478
+ var FRONTMATTER_REGEX = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/;
4479
+ function isSkippableLine(line) {
4480
+ const trimmed = line.trim();
4481
+ return !trimmed || trimmed.startsWith("#");
4482
+ }
4483
+ function parseKeyValue(trimmed) {
4484
+ const colonIndex = trimmed.indexOf(":");
4485
+ if (colonIndex === -1) {
4486
+ return null;
4487
+ }
4488
+ return {
4489
+ key: trimmed.slice(0, colonIndex),
4490
+ value: trimmed.slice(colonIndex + 1).trim()
4374
4491
  };
4375
- if (def.type?._def && def.type?._def?.typeName !== ZodFirstPartyTypeKind.ZodAny) {
4376
- res.items = parseDef(def.type._def, {
4377
- ...refs,
4378
- currentPath: [...refs.currentPath, "items"]
4379
- });
4492
+ }
4493
+ function parseArray(value) {
4494
+ const match = value.match(ARRAY_PATTERN);
4495
+ if (match?.[1] !== void 0) {
4496
+ return match[1].split(",").map((s) => s.trim());
4380
4497
  }
4381
- if (def.minLength) {
4382
- setResponseValueAndErrors(res, "minItems", def.minLength.value, def.minLength.message, refs);
4498
+ return null;
4499
+ }
4500
+ function stripQuotes(value) {
4501
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
4502
+ return value.slice(1, -1);
4383
4503
  }
4384
- if (def.maxLength) {
4385
- setResponseValueAndErrors(res, "maxItems", def.maxLength.value, def.maxLength.message, refs);
4504
+ return value;
4505
+ }
4506
+ function parseYamlValue(value) {
4507
+ const array2 = parseArray(value);
4508
+ if (array2) {
4509
+ return array2;
4386
4510
  }
4387
- if (def.exactLength) {
4388
- setResponseValueAndErrors(res, "minItems", def.exactLength.value, def.exactLength.message, refs);
4389
- setResponseValueAndErrors(res, "maxItems", def.exactLength.value, def.exactLength.message, refs);
4511
+ if (value === "true") {
4512
+ return true;
4390
4513
  }
4391
- return res;
4514
+ if (value === "false") {
4515
+ return false;
4516
+ }
4517
+ if (INTEGER_PATTERN.test(value)) {
4518
+ return Number.parseInt(value, 10);
4519
+ }
4520
+ return stripQuotes(value);
4392
4521
  }
4393
-
4394
- // ../../node_modules/zod-to-json-schema/dist/esm/parsers/bigint.js
4395
- init_esm_shims();
4396
- function parseBigintDef(def, refs) {
4397
- const res = {
4398
- type: "integer",
4399
- format: "int64"
4400
- };
4401
- if (!def.checks)
4402
- return res;
4403
- for (const check2 of def.checks) {
4404
- switch (check2.kind) {
4405
- case "min":
4406
- if (refs.target === "jsonSchema7") {
4407
- if (check2.inclusive) {
4408
- setResponseValueAndErrors(res, "minimum", check2.value, check2.message, refs);
4409
- } else {
4410
- setResponseValueAndErrors(res, "exclusiveMinimum", check2.value, check2.message, refs);
4411
- }
4412
- } else {
4413
- if (!check2.inclusive) {
4414
- res.exclusiveMinimum = true;
4415
- }
4416
- setResponseValueAndErrors(res, "minimum", check2.value, check2.message, refs);
4417
- }
4418
- break;
4419
- case "max":
4420
- if (refs.target === "jsonSchema7") {
4421
- if (check2.inclusive) {
4422
- setResponseValueAndErrors(res, "maximum", check2.value, check2.message, refs);
4423
- } else {
4424
- setResponseValueAndErrors(res, "exclusiveMaximum", check2.value, check2.message, refs);
4425
- }
4426
- } else {
4427
- if (!check2.inclusive) {
4428
- res.exclusiveMaximum = true;
4429
- }
4430
- setResponseValueAndErrors(res, "maximum", check2.value, check2.message, refs);
4431
- }
4432
- break;
4433
- case "multipleOf":
4434
- setResponseValueAndErrors(res, "multipleOf", check2.value, check2.message, refs);
4435
- break;
4522
+ function processYamlLine(state, line) {
4523
+ if (isSkippableLine(line)) {
4524
+ return;
4525
+ }
4526
+ const indent = line.search(NON_SPACE_PATTERN);
4527
+ const trimmed = line.trim();
4528
+ if (indent === 0) {
4529
+ processTopLevel(state, trimmed);
4530
+ } else if (indent === 2 && state.currentKey === "skills") {
4531
+ processSkillItem(state, trimmed);
4532
+ } else if (indent === 2 && state.currentKey === "inputs") {
4533
+ processInputItem(state, trimmed);
4534
+ } else if (indent === 2 && state.currentKey === "steps") {
4535
+ processStepItem(state, trimmed);
4536
+ } else if (indent === 4 && state.currentInput) {
4537
+ processInputProperty(state, trimmed);
4538
+ } else if (indent === 4 && state.currentStep) {
4539
+ processStepProperty(state, trimmed);
4540
+ } else if (indent === 6 && state.currentValidate) {
4541
+ processValidateProperty(state, trimmed);
4542
+ }
4543
+ }
4544
+ function processSkillItem(state, trimmed) {
4545
+ if (trimmed.startsWith("- ")) {
4546
+ const skillName = trimmed.slice(2).trim();
4547
+ if (skillName) {
4548
+ state.skills.push(skillName);
4549
+ }
4550
+ }
4551
+ }
4552
+ function processTopLevel(state, trimmed) {
4553
+ if (state.currentStep?.id) {
4554
+ state.steps.push(state.currentStep);
4555
+ state.currentStep = null;
4556
+ }
4557
+ if (state.currentInput?.name) {
4558
+ state.inputs.push(state.currentInput);
4559
+ state.currentInput = null;
4560
+ }
4561
+ const kv = parseKeyValue(trimmed);
4562
+ if (!kv) {
4563
+ return;
4564
+ }
4565
+ state.currentKey = kv.key;
4566
+ if (kv.key !== "steps" && kv.key !== "inputs" && kv.key !== "skills" && kv.value) {
4567
+ state.result[kv.key] = kv.value;
4568
+ }
4569
+ state.currentValidate = null;
4570
+ }
4571
+ function processInputItem(state, trimmed) {
4572
+ if (trimmed.startsWith("- ")) {
4573
+ if (state.currentInput?.name) {
4574
+ state.inputs.push(state.currentInput);
4575
+ }
4576
+ state.currentInput = {};
4577
+ const firstProp = trimmed.slice(2).trim();
4578
+ const kv = parseKeyValue(firstProp);
4579
+ if (kv) {
4580
+ handleInputProperty(state.currentInput, kv.key, kv.value);
4436
4581
  }
4437
4582
  }
4438
- return res;
4439
4583
  }
4440
-
4441
- // ../../node_modules/zod-to-json-schema/dist/esm/parsers/boolean.js
4442
- init_esm_shims();
4443
- function parseBooleanDef() {
4444
- return {
4445
- type: "boolean"
4446
- };
4584
+ function handleInputProperty(input, key, value) {
4585
+ switch (key) {
4586
+ case "name":
4587
+ input.name = value;
4588
+ break;
4589
+ case "required":
4590
+ input.required = value === "true";
4591
+ break;
4592
+ case "description":
4593
+ input.description = value;
4594
+ break;
4595
+ case "type":
4596
+ if (value === "file" || value === "json") {
4597
+ input.type = value;
4598
+ }
4599
+ break;
4600
+ default:
4601
+ break;
4602
+ }
4447
4603
  }
4448
-
4449
- // ../../node_modules/zod-to-json-schema/dist/esm/parsers/branded.js
4450
- init_esm_shims();
4451
- function parseBrandedDef(_def, refs) {
4452
- return parseDef(_def.type._def, refs);
4604
+ function processInputProperty(state, trimmed) {
4605
+ if (!state.currentInput) {
4606
+ return;
4607
+ }
4608
+ const kv = parseKeyValue(trimmed);
4609
+ if (!kv) {
4610
+ return;
4611
+ }
4612
+ handleInputProperty(state.currentInput, kv.key, kv.value);
4453
4613
  }
4454
-
4455
- // ../../node_modules/zod-to-json-schema/dist/esm/parsers/catch.js
4456
- init_esm_shims();
4457
- var parseCatchDef = (def, refs) => {
4614
+ function processStepItem(state, trimmed) {
4615
+ if (trimmed.startsWith("- ")) {
4616
+ if (state.currentStep?.id) {
4617
+ state.steps.push(state.currentStep);
4618
+ }
4619
+ state.currentStep = {};
4620
+ state.currentValidate = null;
4621
+ const firstProp = trimmed.slice(2).trim();
4622
+ const kv = parseKeyValue(firstProp);
4623
+ if (kv) {
4624
+ handleStepProperty(state.currentStep, kv.key, kv.value);
4625
+ }
4626
+ }
4627
+ }
4628
+ function handleStepProperty(step, key, value) {
4629
+ switch (key) {
4630
+ case "id":
4631
+ step.id = value;
4632
+ break;
4633
+ case "skill":
4634
+ step.skill = value;
4635
+ break;
4636
+ case "mission":
4637
+ step.mission = value;
4638
+ break;
4639
+ case "run":
4640
+ step.run = value;
4641
+ break;
4642
+ case "input": {
4643
+ const arr = parseArray(value);
4644
+ step.input = arr ?? value;
4645
+ break;
4646
+ }
4647
+ case "output":
4648
+ step.output = value;
4649
+ break;
4650
+ case "needs": {
4651
+ const arr = parseArray(value);
4652
+ if (arr) {
4653
+ step.needs = arr;
4654
+ }
4655
+ break;
4656
+ }
4657
+ case "final":
4658
+ step.final = value === "true";
4659
+ break;
4660
+ case "validate": {
4661
+ const validate = {};
4662
+ step.validate = validate;
4663
+ return validate;
4664
+ }
4665
+ default:
4666
+ break;
4667
+ }
4668
+ return null;
4669
+ }
4670
+ function processStepProperty(state, trimmed) {
4671
+ if (!state.currentStep) {
4672
+ return;
4673
+ }
4674
+ const kv = parseKeyValue(trimmed);
4675
+ if (!kv) {
4676
+ return;
4677
+ }
4678
+ const validate = handleStepProperty(state.currentStep, kv.key, kv.value);
4679
+ if (validate) {
4680
+ state.currentValidate = validate;
4681
+ }
4682
+ }
4683
+ function processValidateProperty(state, trimmed) {
4684
+ if (!state.currentValidate) {
4685
+ return;
4686
+ }
4687
+ const kv = parseKeyValue(trimmed);
4688
+ if (!kv) {
4689
+ return;
4690
+ }
4691
+ state.currentValidate[kv.key] = parseYamlValue(kv.value);
4692
+ }
4693
+ function parseFrontmatter(content) {
4694
+ const match = content.match(FRONTMATTER_REGEX);
4695
+ if (!match) {
4696
+ throw new Error(
4697
+ "Invalid workflow file: missing YAML frontmatter (must start with ---)"
4698
+ );
4699
+ }
4700
+ const yamlContent = match[1] ?? "";
4701
+ const body = match[2] ?? "";
4702
+ const frontmatter = parseSimpleYaml(yamlContent);
4703
+ return { frontmatter, body: body.trim() };
4704
+ }
4705
+ function parseSimpleYaml(yaml) {
4706
+ const state = {
4707
+ result: {},
4708
+ steps: [],
4709
+ inputs: [],
4710
+ skills: [],
4711
+ currentKey: "",
4712
+ currentStep: null,
4713
+ currentInput: null,
4714
+ currentValidate: null
4715
+ };
4716
+ for (const line of yaml.split("\n")) {
4717
+ processYamlLine(state, line);
4718
+ }
4719
+ if (state.currentInput?.name) {
4720
+ state.inputs.push(state.currentInput);
4721
+ }
4722
+ if (state.currentStep?.id) {
4723
+ state.steps.push(state.currentStep);
4724
+ }
4725
+ if (state.skills.length > 0) {
4726
+ state.result.skills = state.skills;
4727
+ }
4728
+ if (state.inputs.length > 0) {
4729
+ state.result.inputs = state.inputs;
4730
+ }
4731
+ if (state.steps.length > 0) {
4732
+ state.result.steps = state.steps;
4733
+ }
4734
+ return state.result;
4735
+ }
4736
+ var INPUT_REFERENCE_PATTERN = /\$\{\{\s*inputs\.(\w[\w-]*)\s*\}\}/g;
4737
+ function validateInputReferences(input, workflowInputs) {
4738
+ const inputs = Array.isArray(input) ? input : [input];
4739
+ const declaredNames = new Set(
4740
+ workflowInputs.filter((i) => i.name).map((i) => i.name)
4741
+ );
4742
+ for (const inp of inputs) {
4743
+ const matches = inp.matchAll(INPUT_REFERENCE_PATTERN);
4744
+ for (const match of matches) {
4745
+ const referencedName = match[1];
4746
+ if (referencedName && !declaredNames.has(referencedName)) {
4747
+ throw new Error(
4748
+ `Unknown input reference: inputs.${referencedName}. Declared inputs: ${[...declaredNames].join(", ") || "(none)"}`
4749
+ );
4750
+ }
4751
+ }
4752
+ }
4753
+ }
4754
+ function validateStepExecutionMode(step) {
4755
+ const hasSkill = Boolean(step.skill && step.mission);
4756
+ const hasRun = Boolean(step.run);
4757
+ if (!(hasSkill || hasRun)) {
4758
+ throw new Error(
4759
+ `Step '${step.id}' must have either 'skill' + 'mission' (v0.6.1) or 'run' (v0.6.0 deprecated)`
4760
+ );
4761
+ }
4762
+ if (hasSkill && hasRun) {
4763
+ throw new Error(
4764
+ `Step '${step.id}' cannot have both 'skill' and 'run' - use one or the other`
4765
+ );
4766
+ }
4767
+ if (hasRun && step.run && !isValidRunFormat(step.run)) {
4768
+ throw new Error(
4769
+ `Step '${step.id}' has invalid run format '${step.run}'. Expected 'agents/{name}' where name is lowercase alphanumeric with hyphens.`
4770
+ );
4771
+ }
4772
+ }
4773
+ function validateStep(step, workflowInputs = []) {
4774
+ if (!step.id) {
4775
+ throw new Error("Each step must have an 'id' field");
4776
+ }
4777
+ validateStepExecutionMode(step);
4778
+ const isInputlessCapable = step.skill && INPUTLESS_CAPABLE_SKILLS.includes(step.skill);
4779
+ if (!(step.input || isInputlessCapable)) {
4780
+ throw new Error(
4781
+ `Step '${step.id}' must have an 'input' field (or use an input-less capable skill like: ${INPUTLESS_CAPABLE_SKILLS.join(", ")})`
4782
+ );
4783
+ }
4784
+ if (Array.isArray(step.input) && step.input.length === 0) {
4785
+ throw new Error(`Step '${step.id}' input array cannot be empty`);
4786
+ }
4787
+ if (step.input && workflowInputs.length > 0) {
4788
+ validateInputReferences(step.input, workflowInputs);
4789
+ }
4790
+ if (!step.output) {
4791
+ throw new Error(`Step '${step.id}' must have an 'output' field`);
4792
+ }
4793
+ }
4794
+ function parseWorkflow(content) {
4795
+ const { frontmatter, body } = parseFrontmatter(content);
4796
+ if (!frontmatter.name || typeof frontmatter.name !== "string") {
4797
+ throw new Error("Workflow must have a 'name' field");
4798
+ }
4799
+ if (!frontmatter.description || typeof frontmatter.description !== "string") {
4800
+ throw new Error("Workflow must have a 'description' field");
4801
+ }
4802
+ if (!(frontmatter.steps && Array.isArray(frontmatter.steps)) || frontmatter.steps.length === 0) {
4803
+ throw new Error("Workflow must have at least one step defined");
4804
+ }
4805
+ const workflowInputs = Array.isArray(frontmatter.inputs) ? frontmatter.inputs : [];
4806
+ const workflowSkills = Array.isArray(frontmatter.skills) ? frontmatter.skills : [];
4807
+ for (const step of frontmatter.steps) {
4808
+ validateStep(step, workflowInputs);
4809
+ }
4810
+ const inputs = workflowInputs.filter((inp) => inp.name).map((inp) => ({
4811
+ name: inp.name,
4812
+ required: inp.required ?? true,
4813
+ description: inp.description,
4814
+ type: inp.type
4815
+ }));
4816
+ return {
4817
+ definition: {
4818
+ name: frontmatter.name,
4819
+ version: frontmatter.version,
4820
+ description: frontmatter.description,
4821
+ skills: workflowSkills.length > 0 ? workflowSkills : void 0,
4822
+ inputs: inputs.length > 0 ? inputs : void 0,
4823
+ steps: frontmatter.steps
4824
+ },
4825
+ instructions: body
4826
+ };
4827
+ }
4828
+ function generateValidationManifest(definition) {
4829
+ const steps = {};
4830
+ for (const step of definition.steps) {
4831
+ steps[step.id] = {
4832
+ output: step.output,
4833
+ validate: step.validate,
4834
+ validated: false
4835
+ };
4836
+ }
4837
+ return {
4838
+ workflow: definition.name,
4839
+ version: definition.version,
4840
+ finalStepId: getFinalStep(definition),
4841
+ steps
4842
+ };
4843
+ }
4844
+ function getExecutionOrder(definition) {
4845
+ const stepsMap = new Map(definition.steps.map((s) => [s.id, s]));
4846
+ const order = [];
4847
+ const visited = /* @__PURE__ */ new Set();
4848
+ const visiting = /* @__PURE__ */ new Set();
4849
+ function visit(id) {
4850
+ if (visited.has(id)) {
4851
+ return;
4852
+ }
4853
+ if (visiting.has(id)) {
4854
+ throw new Error(`Circular dependency detected: ${id}`);
4855
+ }
4856
+ visiting.add(id);
4857
+ const step = stepsMap.get(id);
4858
+ if (step?.needs) {
4859
+ for (const dep of step.needs) {
4860
+ if (!stepsMap.has(dep)) {
4861
+ throw new Error(`Unknown dependency '${dep}' in step '${id}'`);
4862
+ }
4863
+ visit(dep);
4864
+ }
4865
+ }
4866
+ visiting.delete(id);
4867
+ visited.add(id);
4868
+ order.push(id);
4869
+ }
4870
+ for (const step of definition.steps) {
4871
+ visit(step.id);
4872
+ }
4873
+ return order;
4874
+ }
4875
+ function getFinalStep(definition) {
4876
+ for (const step of definition.steps) {
4877
+ if (step.final) {
4878
+ return step.id;
4879
+ }
4880
+ }
4881
+ const order = getExecutionOrder(definition);
4882
+ const lastStep = order.at(-1);
4883
+ if (!lastStep) {
4884
+ throw new Error("Workflow has no steps");
4885
+ }
4886
+ return lastStep;
4887
+ }
4888
+ function isInputlessWorkflow(definition) {
4889
+ if (definition.inputs?.some((input) => input.required)) {
4890
+ return false;
4891
+ }
4892
+ const firstSteps = definition.steps.filter(
4893
+ (step) => !step.needs || step.needs.length === 0
4894
+ );
4895
+ if (firstSteps.length === 0) {
4896
+ return false;
4897
+ }
4898
+ return firstSteps.every(
4899
+ (step) => step.skill && INPUTLESS_CAPABLE_SKILLS.includes(step.skill)
4900
+ );
4901
+ }
4902
+ function extractWorkflowSkills(workflow) {
4903
+ if (workflow.definition.skills && workflow.definition.skills.length > 0) {
4904
+ return workflow.definition.skills;
4905
+ }
4906
+ const skills = /* @__PURE__ */ new Set();
4907
+ for (const step of workflow.definition.steps) {
4908
+ if (step.skill) {
4909
+ skills.add(step.skill);
4910
+ }
4911
+ }
4912
+ return [...skills];
4913
+ }
4914
+ var SourceTypeSchema = external_exports.enum([
4915
+ "rss",
4916
+ "youtube",
4917
+ "podcast",
4918
+ "twitter",
4919
+ "custom"
4920
+ ]);
4921
+ var SourceSchema = external_exports.object({
4922
+ id: external_exports.string().min(1),
4923
+ type: SourceTypeSchema,
4924
+ label: external_exports.string().optional(),
4925
+ url: external_exports.string(),
4926
+ metadata: external_exports.record(external_exports.unknown()).optional()
4927
+ });
4928
+ var ContentMetadataSchema = external_exports.object({
4929
+ language: external_exports.string().length(2).optional(),
4930
+ durationSeconds: external_exports.number().positive().optional(),
4931
+ author: external_exports.string().optional(),
4932
+ wordCount: external_exports.number().positive().optional()
4933
+ }).passthrough();
4934
+ var ContentItemSchema = external_exports.object({
4935
+ id: external_exports.string().min(1),
4936
+ source: SourceSchema,
4937
+ title: external_exports.string().min(1),
4938
+ url: external_exports.string(),
4939
+ publishedAt: external_exports.string().optional(),
4940
+ rawText: external_exports.string().min(1),
4941
+ metadata: ContentMetadataSchema
4942
+ });
4943
+ var ValidationCriteriaSchema = external_exports.object({
4944
+ required_fields: external_exports.array(external_exports.string()).optional(),
4945
+ min_quotes: external_exports.number().int().positive().optional(),
4946
+ min_key_points: external_exports.number().int().positive().optional(),
4947
+ min_outline_sections: external_exports.number().int().positive().optional(),
4948
+ has_hooks: external_exports.boolean().optional()
4949
+ }).passthrough();
4950
+ var WorkflowInputSchema = external_exports.object({
4951
+ name: external_exports.string().min(1).regex(/^[a-z][a-z0-9-]*$/, "Input name must be kebab-case"),
4952
+ required: external_exports.boolean(),
4953
+ description: external_exports.string().optional(),
4954
+ type: external_exports.enum(["file", "json"]).optional()
4955
+ });
4956
+ var WorkflowStepSchema = external_exports.object({
4957
+ id: external_exports.string().min(1),
4958
+ skill: external_exports.string().min(1).optional(),
4959
+ mission: external_exports.string().min(1).optional(),
4960
+ run: external_exports.string().min(1).optional(),
4961
+ input: external_exports.union([external_exports.string(), external_exports.array(external_exports.string())]).optional(),
4962
+ output: external_exports.string().min(1),
4963
+ needs: external_exports.array(external_exports.string()).optional(),
4964
+ final: external_exports.boolean().optional(),
4965
+ validate: ValidationCriteriaSchema.optional()
4966
+ }).refine(
4967
+ (step) => step.skill && step.mission || step.run,
4968
+ "Step must have either 'skill' + 'mission' or 'run'"
4969
+ ).refine(
4970
+ (step) => !(step.skill && step.run),
4971
+ "Step cannot have both 'skill' and 'run'"
4972
+ );
4973
+ var WorkflowDefinitionSchema = external_exports.object({
4974
+ name: external_exports.string().min(1),
4975
+ version: external_exports.string().optional(),
4976
+ description: external_exports.string().min(1),
4977
+ inputs: external_exports.array(WorkflowInputSchema).optional(),
4978
+ steps: external_exports.array(WorkflowStepSchema)
4979
+ });
4980
+ var StepValidationStateSchema = external_exports.object({
4981
+ output: external_exports.string().min(1),
4982
+ validate: ValidationCriteriaSchema.optional(),
4983
+ validated: external_exports.boolean()
4984
+ });
4985
+ var ValidationManifestSchema = external_exports.object({
4986
+ workflow: external_exports.string().min(1),
4987
+ version: external_exports.string().optional(),
4988
+ sandboxId: external_exports.string().optional(),
4989
+ createdAt: external_exports.string().optional(),
4990
+ finalStepId: external_exports.string().optional(),
4991
+ steps: external_exports.record(external_exports.string(), StepValidationStateSchema)
4992
+ });
4993
+ var ValidationCheckSchema = external_exports.object({
4994
+ name: external_exports.string().min(1),
4995
+ passed: external_exports.boolean(),
4996
+ message: external_exports.string()
4997
+ });
4998
+ var ValidationResultSchema = external_exports.object({
4999
+ passed: external_exports.boolean(),
5000
+ checks: external_exports.array(ValidationCheckSchema)
5001
+ });
5002
+ var SessionManifestSchema = external_exports.object({
5003
+ version: external_exports.literal(1),
5004
+ contentId: external_exports.string().min(1),
5005
+ pipeline: external_exports.string().min(1),
5006
+ desiredOutput: external_exports.string().min(1),
5007
+ updatedAt: external_exports.string(),
5008
+ steps: external_exports.record(external_exports.string(), external_exports.literal("done"))
5009
+ });
5010
+ var UserTopicSchema = external_exports.object({
5011
+ topic: external_exports.string().min(1),
5012
+ interestLevel: external_exports.union([
5013
+ external_exports.literal(1),
5014
+ external_exports.literal(2),
5015
+ external_exports.literal(3),
5016
+ external_exports.literal(4),
5017
+ external_exports.literal(5)
5018
+ ])
5019
+ });
5020
+ var WritingStyleSchema = external_exports.object({
5021
+ tone: external_exports.enum(["beginner", "intermediate", "expert", "mixed"]),
5022
+ targetWordCount: external_exports.number().min(100).max(1e4),
5023
+ voice: external_exports.enum(["first-person", "third-person", "instructional"])
5024
+ });
5025
+ var UserProfileSchema = external_exports.object({
5026
+ userId: external_exports.string().min(1),
5027
+ topics: external_exports.array(UserTopicSchema),
5028
+ style: WritingStyleSchema,
5029
+ writingSamples: external_exports.array(external_exports.string()).optional()
5030
+ });
5031
+ function validateUserProfile(data) {
5032
+ const result = UserProfileSchema.safeParse(data);
5033
+ if (result.success) {
5034
+ return { success: true, data: result.data };
5035
+ }
5036
+ return { success: false, error: { message: result.error.message } };
5037
+ }
5038
+
5039
+ // ../../packages/provider/dist/chunk-W4MDOBYT.js
5040
+ init_esm_shims();
5041
+ import { execSync } from "child_process";
5042
+ import { existsSync as existsSync3 } from "fs";
5043
+ import { createRequire } from "module";
5044
+ import { homedir as homedir3 } from "os";
5045
+ import { dirname as dirname3, join as join4 } from "path";
5046
+ import {
5047
+ chmod,
5048
+ mkdir as mkdir3,
5049
+ readFile as readFile2,
5050
+ rename,
5051
+ rm as rm2,
5052
+ writeFile as writeFile2
5053
+ } from "fs/promises";
5054
+ import { homedir as homedir22 } from "os";
5055
+ import { join as join23 } from "path";
5056
+
5057
+ // ../../node_modules/zod-to-json-schema/dist/esm/index.js
5058
+ init_esm_shims();
5059
+
5060
+ // ../../node_modules/zod-to-json-schema/dist/esm/Options.js
5061
+ init_esm_shims();
5062
+ var ignoreOverride = /* @__PURE__ */ Symbol("Let zodToJsonSchema decide on which parser to use");
5063
+ var defaultOptions = {
5064
+ name: void 0,
5065
+ $refStrategy: "root",
5066
+ basePath: ["#"],
5067
+ effectStrategy: "input",
5068
+ pipeStrategy: "all",
5069
+ dateStrategy: "format:date-time",
5070
+ mapStrategy: "entries",
5071
+ removeAdditionalStrategy: "passthrough",
5072
+ allowedAdditionalProperties: true,
5073
+ rejectedAdditionalProperties: false,
5074
+ definitionPath: "definitions",
5075
+ target: "jsonSchema7",
5076
+ strictUnions: false,
5077
+ definitions: {},
5078
+ errorMessages: false,
5079
+ markdownDescription: false,
5080
+ patternStrategy: "escape",
5081
+ applyRegexFlags: false,
5082
+ emailStrategy: "format:email",
5083
+ base64Strategy: "contentEncoding:base64",
5084
+ nameStrategy: "ref",
5085
+ openAiAnyTypeName: "OpenAiAnyType"
5086
+ };
5087
+ var getDefaultOptions = (options) => typeof options === "string" ? {
5088
+ ...defaultOptions,
5089
+ name: options
5090
+ } : {
5091
+ ...defaultOptions,
5092
+ ...options
5093
+ };
5094
+
5095
+ // ../../node_modules/zod-to-json-schema/dist/esm/Refs.js
5096
+ init_esm_shims();
5097
+ var getRefs = (options) => {
5098
+ const _options = getDefaultOptions(options);
5099
+ const currentPath = _options.name !== void 0 ? [..._options.basePath, _options.definitionPath, _options.name] : _options.basePath;
5100
+ return {
5101
+ ..._options,
5102
+ flags: { hasReferencedOpenAiAnyType: false },
5103
+ currentPath,
5104
+ propertyPath: void 0,
5105
+ seen: new Map(Object.entries(_options.definitions).map(([name, def]) => [
5106
+ def._def,
5107
+ {
5108
+ def: def._def,
5109
+ path: [..._options.basePath, _options.definitionPath, name],
5110
+ // Resolution of references will be forced even though seen, so it's ok that the schema is undefined here for now.
5111
+ jsonSchema: void 0
5112
+ }
5113
+ ]))
5114
+ };
5115
+ };
5116
+
5117
+ // ../../node_modules/zod-to-json-schema/dist/esm/errorMessages.js
5118
+ init_esm_shims();
5119
+ function addErrorMessage(res, key, errorMessage, refs) {
5120
+ if (!refs?.errorMessages)
5121
+ return;
5122
+ if (errorMessage) {
5123
+ res.errorMessage = {
5124
+ ...res.errorMessage,
5125
+ [key]: errorMessage
5126
+ };
5127
+ }
5128
+ }
5129
+ function setResponseValueAndErrors(res, key, value, errorMessage, refs) {
5130
+ res[key] = value;
5131
+ addErrorMessage(res, key, errorMessage, refs);
5132
+ }
5133
+
5134
+ // ../../node_modules/zod-to-json-schema/dist/esm/getRelativePath.js
5135
+ init_esm_shims();
5136
+ var getRelativePath = (pathA, pathB) => {
5137
+ let i = 0;
5138
+ for (; i < pathA.length && i < pathB.length; i++) {
5139
+ if (pathA[i] !== pathB[i])
5140
+ break;
5141
+ }
5142
+ return [(pathA.length - i).toString(), ...pathB.slice(i)].join("/");
5143
+ };
5144
+
5145
+ // ../../node_modules/zod-to-json-schema/dist/esm/parseDef.js
5146
+ init_esm_shims();
5147
+
5148
+ // ../../node_modules/zod-to-json-schema/dist/esm/selectParser.js
5149
+ init_esm_shims();
5150
+
5151
+ // ../../node_modules/zod-to-json-schema/dist/esm/parsers/any.js
5152
+ init_esm_shims();
5153
+ function parseAnyDef(refs) {
5154
+ if (refs.target !== "openAi") {
5155
+ return {};
5156
+ }
5157
+ const anyDefinitionPath = [
5158
+ ...refs.basePath,
5159
+ refs.definitionPath,
5160
+ refs.openAiAnyTypeName
5161
+ ];
5162
+ refs.flags.hasReferencedOpenAiAnyType = true;
5163
+ return {
5164
+ $ref: refs.$refStrategy === "relative" ? getRelativePath(anyDefinitionPath, refs.currentPath) : anyDefinitionPath.join("/")
5165
+ };
5166
+ }
5167
+
5168
+ // ../../node_modules/zod-to-json-schema/dist/esm/parsers/array.js
5169
+ init_esm_shims();
5170
+ function parseArrayDef(def, refs) {
5171
+ const res = {
5172
+ type: "array"
5173
+ };
5174
+ if (def.type?._def && def.type?._def?.typeName !== ZodFirstPartyTypeKind.ZodAny) {
5175
+ res.items = parseDef(def.type._def, {
5176
+ ...refs,
5177
+ currentPath: [...refs.currentPath, "items"]
5178
+ });
5179
+ }
5180
+ if (def.minLength) {
5181
+ setResponseValueAndErrors(res, "minItems", def.minLength.value, def.minLength.message, refs);
5182
+ }
5183
+ if (def.maxLength) {
5184
+ setResponseValueAndErrors(res, "maxItems", def.maxLength.value, def.maxLength.message, refs);
5185
+ }
5186
+ if (def.exactLength) {
5187
+ setResponseValueAndErrors(res, "minItems", def.exactLength.value, def.exactLength.message, refs);
5188
+ setResponseValueAndErrors(res, "maxItems", def.exactLength.value, def.exactLength.message, refs);
5189
+ }
5190
+ return res;
5191
+ }
5192
+
5193
+ // ../../node_modules/zod-to-json-schema/dist/esm/parsers/bigint.js
5194
+ init_esm_shims();
5195
+ function parseBigintDef(def, refs) {
5196
+ const res = {
5197
+ type: "integer",
5198
+ format: "int64"
5199
+ };
5200
+ if (!def.checks)
5201
+ return res;
5202
+ for (const check2 of def.checks) {
5203
+ switch (check2.kind) {
5204
+ case "min":
5205
+ if (refs.target === "jsonSchema7") {
5206
+ if (check2.inclusive) {
5207
+ setResponseValueAndErrors(res, "minimum", check2.value, check2.message, refs);
5208
+ } else {
5209
+ setResponseValueAndErrors(res, "exclusiveMinimum", check2.value, check2.message, refs);
5210
+ }
5211
+ } else {
5212
+ if (!check2.inclusive) {
5213
+ res.exclusiveMinimum = true;
5214
+ }
5215
+ setResponseValueAndErrors(res, "minimum", check2.value, check2.message, refs);
5216
+ }
5217
+ break;
5218
+ case "max":
5219
+ if (refs.target === "jsonSchema7") {
5220
+ if (check2.inclusive) {
5221
+ setResponseValueAndErrors(res, "maximum", check2.value, check2.message, refs);
5222
+ } else {
5223
+ setResponseValueAndErrors(res, "exclusiveMaximum", check2.value, check2.message, refs);
5224
+ }
5225
+ } else {
5226
+ if (!check2.inclusive) {
5227
+ res.exclusiveMaximum = true;
5228
+ }
5229
+ setResponseValueAndErrors(res, "maximum", check2.value, check2.message, refs);
5230
+ }
5231
+ break;
5232
+ case "multipleOf":
5233
+ setResponseValueAndErrors(res, "multipleOf", check2.value, check2.message, refs);
5234
+ break;
5235
+ }
5236
+ }
5237
+ return res;
5238
+ }
5239
+
5240
+ // ../../node_modules/zod-to-json-schema/dist/esm/parsers/boolean.js
5241
+ init_esm_shims();
5242
+ function parseBooleanDef() {
5243
+ return {
5244
+ type: "boolean"
5245
+ };
5246
+ }
5247
+
5248
+ // ../../node_modules/zod-to-json-schema/dist/esm/parsers/branded.js
5249
+ init_esm_shims();
5250
+ function parseBrandedDef(_def, refs) {
5251
+ return parseDef(_def.type._def, refs);
5252
+ }
5253
+
5254
+ // ../../node_modules/zod-to-json-schema/dist/esm/parsers/catch.js
5255
+ init_esm_shims();
5256
+ var parseCatchDef = (def, refs) => {
4458
5257
  return parseDef(def.innerType._def, refs);
4459
5258
  };
4460
5259
 
@@ -5592,6 +6391,9 @@ var zodToJsonSchema = (schema, options) => {
5592
6391
  return combined;
5593
6392
  };
5594
6393
 
6394
+ // ../../packages/provider/dist/chunk-W4MDOBYT.js
6395
+ import { join as join6 } from "path";
6396
+
5595
6397
  // ../../node_modules/@anthropic-ai/claude-agent-sdk/sdk.mjs
5596
6398
  init_esm_shims();
5597
6399
  import { join as join5 } from "path";
@@ -5628,7 +6430,7 @@ var __toESM = (mod, isNodeMode, target) => {
5628
6430
  return to;
5629
6431
  };
5630
6432
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
5631
- var __export = (target, all) => {
6433
+ var __export2 = (target, all) => {
5632
6434
  for (var name in all)
5633
6435
  __defProp(target, name, {
5634
6436
  get: all[name],
@@ -17600,7 +18402,7 @@ function config(newConfig) {
17600
18402
  return globalConfig;
17601
18403
  }
17602
18404
  var exports_util = {};
17603
- __export(exports_util, {
18405
+ __export2(exports_util, {
17604
18406
  unwrapMessage: () => unwrapMessage,
17605
18407
  stringifyPrimitive: () => stringifyPrimitive,
17606
18408
  required: () => required,
@@ -20528,7 +21330,7 @@ function _refine(Class2, fn, _params) {
20528
21330
  return schema;
20529
21331
  }
20530
21332
  var exports_iso2 = {};
20531
- __export(exports_iso2, {
21333
+ __export2(exports_iso2, {
20532
21334
  time: () => time2,
20533
21335
  duration: () => duration2,
20534
21336
  datetime: () => datetime2,
@@ -22127,12 +22929,27 @@ function query({
22127
22929
  return queryInstance;
22128
22930
  }
22129
22931
 
22130
- // ../../packages/provider/dist/chunk-3N4TZG2S.js
22932
+ // ../../packages/provider/dist/chunk-W4MDOBYT.js
22131
22933
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync3, writeFileSync } from "fs";
22132
22934
  import { join as join33 } from "path";
22133
- import { cp as cp2, mkdir as mkdir22, readFile as readFile22, rm as rm22, writeFile as writeFile22 } from "fs/promises";
22935
+ import { access, readdir as readdir3, readFile as readFile22, stat } from "fs/promises";
22936
+ import { join as join42 } from "path";
22937
+ import { cp as cp2, mkdir as mkdir22, readFile as readFile3, rm as rm22, writeFile as writeFile22 } from "fs/promises";
22134
22938
  import { homedir as homedir32 } from "os";
22135
- import { isAbsolute, join as join42, normalize, resolve } from "path";
22939
+ import { isAbsolute, join as join52, normalize, resolve } from "path";
22940
+ import { readdir as readdir22, readFile as readFile4, rename as rename2, stat as stat2, writeFile as writeFile3 } from "fs/promises";
22941
+ import { join as join7 } from "path";
22942
+ import {
22943
+ open as open2,
22944
+ readdir as readdir32,
22945
+ readFile as readFile5,
22946
+ rename as rename3,
22947
+ stat as stat3,
22948
+ unlink,
22949
+ writeFile as writeFile4
22950
+ } from "fs/promises";
22951
+ import { join as join8 } from "path";
22952
+ import { join as join9 } from "path";
22136
22953
  var LINE_SPLIT_REGEX = /\r?\n/;
22137
22954
  function findSdkBundledCliPath() {
22138
22955
  try {
@@ -22226,11 +23043,42 @@ var PRESETS = {
22226
23043
  ANTHROPIC_CLAUDE_SONNET: {
22227
23044
  name: "Anthropic Claude Sonnet",
22228
23045
  apiProvider: "anthropic",
22229
- mainModel: "claude-sonnet-4-5-20250514",
22230
- executorModel: "claude-sonnet-4-5-20250514",
22231
- haikuModel: "claude-sonnet-4-5-20250514",
22232
- sonnetModel: "claude-sonnet-4-5-20250514",
22233
- opusModel: "claude-sonnet-4-5-20250514"
23046
+ mainModel: "claude-sonnet-4-5-20250929",
23047
+ executorModel: "claude-sonnet-4-5-20250929",
23048
+ haikuModel: "claude-sonnet-4-5-20250929",
23049
+ sonnetModel: "claude-sonnet-4-5-20250929",
23050
+ opusModel: "claude-sonnet-4-5-20250929"
23051
+ },
23052
+ // Claude Code Subscription (macOS Keychain)
23053
+ CLAUDE_CODE_SUBSCRIPTION_HAIKU: {
23054
+ name: "Claude Code Subscription (Haiku)",
23055
+ apiProvider: "anthropic",
23056
+ authTokenSource: "subscription",
23057
+ mainModel: "claude-haiku-4-5-20251001",
23058
+ executorModel: "claude-haiku-4-5-20251001",
23059
+ haikuModel: "claude-haiku-4-5-20251001",
23060
+ sonnetModel: "claude-haiku-4-5-20251001",
23061
+ opusModel: "claude-haiku-4-5-20251001"
23062
+ },
23063
+ CLAUDE_CODE_SUBSCRIPTION_SONNET: {
23064
+ name: "Claude Code Subscription (Sonnet)",
23065
+ apiProvider: "anthropic",
23066
+ authTokenSource: "subscription",
23067
+ mainModel: "claude-sonnet-4-5-20250929",
23068
+ executorModel: "claude-sonnet-4-5-20250929",
23069
+ haikuModel: "claude-sonnet-4-5-20250929",
23070
+ sonnetModel: "claude-sonnet-4-5-20250929",
23071
+ opusModel: "claude-sonnet-4-5-20250929"
23072
+ },
23073
+ CLAUDE_CODE_SUBSCRIPTION_OPUS: {
23074
+ name: "Claude Code Subscription (Opus)",
23075
+ apiProvider: "anthropic",
23076
+ authTokenSource: "subscription",
23077
+ mainModel: "claude-opus-4-5-20251101",
23078
+ executorModel: "claude-opus-4-5-20251101",
23079
+ haikuModel: "claude-opus-4-5-20251101",
23080
+ sonnetModel: "claude-opus-4-5-20251101",
23081
+ opusModel: "claude-opus-4-5-20251101"
22234
23082
  },
22235
23083
  // ZenMux Presets
22236
23084
  ZENMUX_ANTHROPIC_HAIKU45: {
@@ -22253,6 +23101,16 @@ var PRESETS = {
22253
23101
  sonnetModel: "z-ai/glm-4.7",
22254
23102
  opusModel: "z-ai/glm-4.7"
22255
23103
  },
23104
+ ZENMUX_ZAI_GLM47FLASHX: {
23105
+ name: "ZenMux GLM-4.7-FlashX",
23106
+ apiProvider: "zenmux",
23107
+ baseUrl: "https://zenmux.ai/api/anthropic",
23108
+ mainModel: "z-ai/glm-4.7-flashx",
23109
+ executorModel: "z-ai/glm-4.7-flashx",
23110
+ haikuModel: "z-ai/glm-4.7-flashx",
23111
+ sonnetModel: "z-ai/glm-4.7-flashx",
23112
+ opusModel: "z-ai/glm-4.7-flashx"
23113
+ },
22256
23114
  ZENMUX_MINIMAX_M21: {
22257
23115
  name: "ZenMux MiniMax-M2.1",
22258
23116
  apiProvider: "zenmux",
@@ -22372,6 +23230,48 @@ var PRESETS = {
22372
23230
  haikuModel: "openai/gpt-5.1-codex-mini",
22373
23231
  sonnetModel: "openai/gpt-5.1-codex-mini",
22374
23232
  opusModel: "openai/gpt-5.1-codex-mini"
23233
+ },
23234
+ // OpenRouter Presets
23235
+ OPENROUTER_PRESET: {
23236
+ name: "OpenRouter (User-Configured Preset)",
23237
+ apiProvider: "openrouter",
23238
+ baseUrl: "https://openrouter.ai/api",
23239
+ mainModel: "@preset/looplia-default",
23240
+ executorModel: "@preset/looplia-default",
23241
+ haikuModel: "@preset/looplia-default",
23242
+ sonnetModel: "@preset/looplia-default",
23243
+ opusModel: "@preset/looplia-default"
23244
+ },
23245
+ OPENROUTER_ZAI_GLM47FLASH: {
23246
+ name: "OpenRouter GLM-4.7-Flash",
23247
+ apiProvider: "openrouter",
23248
+ baseUrl: "https://openrouter.ai/api",
23249
+ mainModel: "z-ai/glm-4.7-flash",
23250
+ executorModel: "z-ai/glm-4.7-flash",
23251
+ haikuModel: "z-ai/glm-4.7-flash",
23252
+ sonnetModel: "z-ai/glm-4.7-flash",
23253
+ opusModel: "z-ai/glm-4.7-flash"
23254
+ },
23255
+ // Ollama Cloud Presets
23256
+ OLLAMA_GLM47_CLOUD: {
23257
+ name: "Ollama GLM-4.7 Cloud",
23258
+ apiProvider: "ollama",
23259
+ baseUrl: "http://localhost:11434",
23260
+ mainModel: "glm-4.7:cloud",
23261
+ executorModel: "glm-4.7:cloud",
23262
+ haikuModel: "glm-4.7:cloud",
23263
+ sonnetModel: "glm-4.7:cloud",
23264
+ opusModel: "glm-4.7:cloud"
23265
+ },
23266
+ OLLAMA_MINIMAX_M21_CLOUD: {
23267
+ name: "Ollama MiniMax-M2.1 Cloud",
23268
+ apiProvider: "ollama",
23269
+ baseUrl: "http://localhost:11434",
23270
+ mainModel: "minimax-m2.1:cloud",
23271
+ executorModel: "minimax-m2.1:cloud",
23272
+ haikuModel: "minimax-m2.1:cloud",
23273
+ sonnetModel: "minimax-m2.1:cloud",
23274
+ opusModel: "minimax-m2.1:cloud"
22375
23275
  }
22376
23276
  };
22377
23277
  var CONFIG_FILE = "looplia.setting.json";
@@ -22423,19 +23323,45 @@ function injectModelTierEnv(mainModel, executorModel) {
22423
23323
  setEnvIfNotSet("LOOPLIA_AGENT_MODEL_MAIN", mainModel);
22424
23324
  setEnvIfNotSet("LOOPLIA_AGENT_MODEL_EXECUTOR", executorModel);
22425
23325
  }
22426
- function injectLoopliaSettingsEnv(settings) {
22427
- if (settings.apiProvider.type !== "anthropic") {
22428
- if (settings.apiProvider.baseUrl && !process.env.ANTHROPIC_BASE_URL) {
22429
- process.env.ANTHROPIC_BASE_URL = settings.apiProvider.baseUrl;
22430
- }
22431
- if (settings.apiProvider.authToken) {
22432
- process.env.ANTHROPIC_API_KEY = settings.apiProvider.authToken;
23326
+ function injectSubscriptionAuth() {
23327
+ if (!process.env.CLAUDE_CODE_OAUTH_TOKEN) {
23328
+ console.warn(
23329
+ "Warning: CLAUDE_CODE_OAUTH_TOKEN not set. Set this environment variable to use Claude Code subscription auth."
23330
+ );
23331
+ }
23332
+ process.env.ANTHROPIC_API_KEY = void 0;
23333
+ }
23334
+ function injectNonAnthropicProviderEnv(apiProvider) {
23335
+ if (apiProvider.baseUrl && !process.env.ANTHROPIC_BASE_URL) {
23336
+ process.env.ANTHROPIC_BASE_URL = apiProvider.baseUrl;
23337
+ }
23338
+ const isOpenRouterEndpoint = apiProvider.type === "openrouter" || apiProvider.baseUrl?.includes("openrouter.ai");
23339
+ const isZenmuxEndpoint = apiProvider.type === "zenmux" || apiProvider.baseUrl?.includes("zenmux.ai");
23340
+ const isOllamaEndpoint = apiProvider.type === "ollama" || apiProvider.baseUrl?.includes("localhost:11434");
23341
+ if (apiProvider.authToken) {
23342
+ if (isOpenRouterEndpoint) {
23343
+ process.env.ANTHROPIC_AUTH_TOKEN = apiProvider.authToken;
23344
+ process.env.ANTHROPIC_API_KEY = void 0;
22433
23345
  } else {
22434
- const isZenmuxEndpoint = settings.apiProvider.type === "zenmux" || settings.apiProvider.baseUrl?.includes("zenmux.ai");
22435
- if (isZenmuxEndpoint && process.env.ZENMUX_API_KEY) {
22436
- process.env.ANTHROPIC_API_KEY = process.env.ZENMUX_API_KEY;
22437
- }
23346
+ process.env.ANTHROPIC_API_KEY = apiProvider.authToken;
22438
23347
  }
23348
+ return;
23349
+ }
23350
+ if (isZenmuxEndpoint && process.env.ZENMUX_API_KEY) {
23351
+ process.env.ANTHROPIC_API_KEY = process.env.ZENMUX_API_KEY;
23352
+ } else if (isOpenRouterEndpoint && process.env.OPENROUTER_API_KEY) {
23353
+ process.env.ANTHROPIC_AUTH_TOKEN = process.env.OPENROUTER_API_KEY;
23354
+ process.env.ANTHROPIC_API_KEY = void 0;
23355
+ } else if (isOllamaEndpoint) {
23356
+ process.env.ANTHROPIC_API_KEY = process.env.OLLAMA_API_KEY || "ollama";
23357
+ }
23358
+ }
23359
+ function injectLoopliaSettingsEnv(settings) {
23360
+ if (settings.apiProvider.authTokenSource === "subscription") {
23361
+ injectSubscriptionAuth();
23362
+ }
23363
+ if (settings.apiProvider.type !== "anthropic") {
23364
+ injectNonAnthropicProviderEnv(settings.apiProvider);
22439
23365
  }
22440
23366
  injectModelTierEnv(settings.agents.main, settings.agents.executor);
22441
23367
  }
@@ -22456,6 +23382,7 @@ function getSettingsDisplayInfo(settings) {
22456
23382
  preset: settings.preset,
22457
23383
  provider,
22458
23384
  authToken: settings.apiProvider.authToken,
23385
+ authTokenSource: settings.apiProvider.authTokenSource,
22459
23386
  agents: {
22460
23387
  main: settings.agents.main,
22461
23388
  executor: settings.agents.executor
@@ -22479,7 +23406,8 @@ function applyPreset(presetName, existingSettings) {
22479
23406
  apiProvider: {
22480
23407
  type: preset.apiProvider,
22481
23408
  baseUrl: preset.baseUrl,
22482
- authToken: existingSettings?.apiProvider.authToken
23409
+ authToken: existingSettings?.apiProvider.authToken,
23410
+ authTokenSource: preset.authTokenSource
22483
23411
  },
22484
23412
  agents: {
22485
23413
  main: preset.mainModel,
@@ -22498,17 +23426,19 @@ async function initializeCommandEnvironment(options = {}) {
22498
23426
  return { settings };
22499
23427
  }
22500
23428
  function validateApiKeyPresence() {
22501
- if (!(process.env.ANTHROPIC_API_KEY || process.env.CLAUDE_CODE_OAUTH_TOKEN)) {
23429
+ if (!(process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN || process.env.CLAUDE_CODE_OAUTH_TOKEN)) {
22502
23430
  console.error("Error: API key required");
22503
23431
  console.error("");
22504
23432
  console.error("Options:");
22505
23433
  console.error(" 1. Set ANTHROPIC_API_KEY environment variable");
22506
23434
  console.error(" 2. Set ZENMUX_API_KEY with a ZenMux preset");
22507
- console.error(" 3. Configure via: looplia config provider preset <name>");
22508
- console.error(" 4. Use --mock flag for testing without API");
23435
+ console.error(" 3. Set OPENROUTER_API_KEY with an OpenRouter preset");
23436
+ console.error(" 4. Configure via: looplia config provider preset <name>");
23437
+ console.error(" 5. Use --mock flag for testing without API");
22509
23438
  console.error("");
22510
23439
  console.error("Get your API key from: https://console.anthropic.com");
22511
23440
  console.error("Or use ZenMux at: https://zenmux.ai");
23441
+ console.error("Or use OpenRouter at: https://openrouter.ai");
22512
23442
  process.exit(1);
22513
23443
  }
22514
23444
  }
@@ -22640,58 +23570,261 @@ function mapException(error2) {
22640
23570
  };
22641
23571
  }
22642
23572
  return {
22643
- success: false,
22644
- error: {
22645
- type: "unknown",
22646
- message: String(error2)
23573
+ success: false,
23574
+ error: {
23575
+ type: "unknown",
23576
+ message: String(error2)
23577
+ }
23578
+ };
23579
+ }
23580
+ var SANDBOX_ID_REGEX = /sandbox\/([^\s/]+)/;
23581
+ var CONTENT_ID_REGEX = /contentItem\/([^\s/]+)/;
23582
+ var SANDBOX_ARG_REGEX = /--sandbox-id\s+([^\s]+)/;
23583
+ var SANDBOX_RESUME_REGEX = /--sandbox\s+([^\s]+)/;
23584
+ var VALID_SANDBOX_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
23585
+ function extractSandboxIdFromPrompt(prompt) {
23586
+ let match = prompt.match(SANDBOX_ID_REGEX);
23587
+ if (!match) {
23588
+ match = prompt.match(SANDBOX_ARG_REGEX);
23589
+ }
23590
+ if (!match) {
23591
+ match = prompt.match(SANDBOX_RESUME_REGEX);
23592
+ }
23593
+ if (!match) {
23594
+ match = prompt.match(CONTENT_ID_REGEX);
23595
+ }
23596
+ const rawId = match?.[1];
23597
+ if (!rawId) {
23598
+ return null;
23599
+ }
23600
+ let decodedId;
23601
+ try {
23602
+ decodedId = decodeURIComponent(rawId);
23603
+ } catch {
23604
+ return null;
23605
+ }
23606
+ if (decodedId.includes("\0")) {
23607
+ return null;
23608
+ }
23609
+ if (decodedId.includes("..") || decodedId.includes("/") || decodedId.includes("\\")) {
23610
+ return null;
23611
+ }
23612
+ if (!VALID_SANDBOX_ID_PATTERN.test(decodedId)) {
23613
+ return null;
23614
+ }
23615
+ return decodedId;
23616
+ }
23617
+ var extractContentIdFromPrompt = extractSandboxIdFromPrompt;
23618
+ var LOCK_MAX_RETRIES = 5;
23619
+ var LOCK_RETRY_DELAY_MS = 200;
23620
+ function isBuildManifest(value) {
23621
+ if (!value || typeof value !== "object") {
23622
+ return false;
23623
+ }
23624
+ const obj = value;
23625
+ return obj.type === "build";
23626
+ }
23627
+ function isRunValidationManifest(value) {
23628
+ if (!value || typeof value !== "object") {
23629
+ return false;
23630
+ }
23631
+ const obj = value;
23632
+ return typeof obj.workflow === "string" && typeof obj.steps === "object" && obj.steps !== null;
23633
+ }
23634
+ function isValidationManifest(value) {
23635
+ return isBuildManifest(value) || isRunValidationManifest(value);
23636
+ }
23637
+ async function sleep(ms) {
23638
+ await new Promise((resolve2) => setTimeout(resolve2, ms));
23639
+ }
23640
+ async function lockExists(lockPath) {
23641
+ try {
23642
+ await access(lockPath);
23643
+ return true;
23644
+ } catch {
23645
+ return false;
23646
+ }
23647
+ }
23648
+ function validationError(field, message) {
23649
+ return {
23650
+ success: false,
23651
+ error: { type: "validation_error", field, message }
23652
+ };
23653
+ }
23654
+ async function findMostRecentSandbox(sandboxRoot) {
23655
+ try {
23656
+ const entries = await readdir3(sandboxRoot, { withFileTypes: true });
23657
+ const dirs = entries.filter(
23658
+ (e) => e.isDirectory() && !e.name.startsWith(".")
23659
+ );
23660
+ if (dirs.length === 0) {
23661
+ return;
23662
+ }
23663
+ const dirStats = await Promise.all(
23664
+ dirs.map(async (dir) => {
23665
+ const dirPath = join42(sandboxRoot, dir.name);
23666
+ const dirStat = await stat(dirPath);
23667
+ return { path: dirPath, mtime: dirStat.mtime.getTime() };
23668
+ })
23669
+ );
23670
+ dirStats.sort((a, b) => b.mtime - a.mtime);
23671
+ return dirStats[0]?.path;
23672
+ } catch {
23673
+ return;
23674
+ }
23675
+ }
23676
+ function findFinalStep(steps) {
23677
+ const stepEntries = Object.entries(steps);
23678
+ if (stepEntries.length === 0) {
23679
+ return;
23680
+ }
23681
+ const lastEntry = stepEntries.at(-1);
23682
+ if (!lastEntry) {
23683
+ return;
23684
+ }
23685
+ return { id: lastEntry[0], output: lastEntry[1].output };
23686
+ }
23687
+ function resolveFinalStep(steps, finalStepId) {
23688
+ if (finalStepId) {
23689
+ const step = steps[finalStepId];
23690
+ if (step?.output) {
23691
+ return { id: finalStepId, output: step.output };
23692
+ }
23693
+ }
23694
+ return findFinalStep(steps);
23695
+ }
23696
+ function areAllStepsValidated(steps) {
23697
+ const pendingSteps = [];
23698
+ for (const [stepId, stepState] of Object.entries(steps)) {
23699
+ if (!stepState.validated) {
23700
+ pendingSteps.push(stepId);
23701
+ }
23702
+ }
23703
+ return {
23704
+ allValidated: pendingSteps.length === 0,
23705
+ pendingSteps
23706
+ };
23707
+ }
23708
+ function resolveSandboxDir(sandboxRoot, sandboxId) {
23709
+ if (sandboxId) {
23710
+ return join42(sandboxRoot, sandboxId);
23711
+ }
23712
+ return findMostRecentSandbox(sandboxRoot);
23713
+ }
23714
+ async function readValidationManifest(sandboxDir) {
23715
+ const validationPath = join42(sandboxDir, "validation.json");
23716
+ const lockPath = `${validationPath}.lock`;
23717
+ for (let attempt = 0; attempt < LOCK_MAX_RETRIES; attempt++) {
23718
+ if (await lockExists(lockPath)) {
23719
+ await sleep(LOCK_RETRY_DELAY_MS);
23720
+ continue;
23721
+ }
23722
+ try {
23723
+ const content = await readFile22(validationPath, "utf-8");
23724
+ const parsed = JSON.parse(content);
23725
+ if (!isValidationManifest(parsed)) {
23726
+ return { error: "Invalid validation manifest structure" };
23727
+ }
23728
+ return { manifest: parsed };
23729
+ } catch (error2) {
23730
+ const message = error2 instanceof Error ? error2.message : String(error2);
23731
+ return { error: `Failed to read validation.json: ${message}` };
23732
+ }
23733
+ }
23734
+ return { error: "Timeout waiting for validation.json lock to release" };
23735
+ }
23736
+ async function readFinalArtifact(sandboxDir, outputPath) {
23737
+ const artifactPath = join42(sandboxDir, outputPath);
23738
+ try {
23739
+ const content = await readFile22(artifactPath, "utf-8");
23740
+ return { artifact: JSON.parse(content) };
23741
+ } catch (error2) {
23742
+ const errorMessage = `Failed to read final artifact from ${outputPath}: ${error2 instanceof Error ? error2.message : String(error2)}`;
23743
+ const isParseError = error2 instanceof Error && error2.message.includes("JSON");
23744
+ if (isParseError) {
23745
+ return {
23746
+ error: {
23747
+ success: false,
23748
+ error: {
23749
+ type: "malformed_output",
23750
+ expected: "valid JSON",
23751
+ got: "invalid JSON",
23752
+ message: errorMessage
23753
+ }
23754
+ }
23755
+ };
23756
+ }
23757
+ return { error: validationError("artifact", errorMessage) };
23758
+ }
23759
+ }
23760
+ function extractBuildResult(manifest, sandboxDir) {
23761
+ if (!manifest.workflowValidated) {
23762
+ return validationError("build", manifest.error ?? "Workflow not validated");
23763
+ }
23764
+ return {
23765
+ success: true,
23766
+ data: {
23767
+ status: "success",
23768
+ sandboxId: manifest.sandboxId ?? sandboxDir.split("/").pop(),
23769
+ workflowName: manifest.workflowName,
23770
+ workflowPath: manifest.workflowPath
22647
23771
  }
22648
23772
  };
22649
23773
  }
22650
- var SANDBOX_ID_REGEX = /sandbox\/([^\s/]+)/;
22651
- var CONTENT_ID_REGEX = /contentItem\/([^\s/]+)/;
22652
- var SANDBOX_ARG_REGEX = /--sandbox-id\s+([^\s]+)/;
22653
- var SANDBOX_RESUME_REGEX = /--sandbox\s+([^\s]+)/;
22654
- var VALID_SANDBOX_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
22655
- function extractSandboxIdFromPrompt(prompt) {
22656
- let match = prompt.match(SANDBOX_ID_REGEX);
22657
- if (!match) {
22658
- match = prompt.match(SANDBOX_ARG_REGEX);
22659
- }
22660
- if (!match) {
22661
- match = prompt.match(SANDBOX_RESUME_REGEX);
22662
- }
22663
- if (!match) {
22664
- match = prompt.match(CONTENT_ID_REGEX);
23774
+ async function extractRunResult(manifest, sandboxDir) {
23775
+ const { allValidated, pendingSteps } = areAllStepsValidated(manifest.steps);
23776
+ if (!allValidated) {
23777
+ return validationError(
23778
+ "steps",
23779
+ `Workflow incomplete. Pending steps: ${pendingSteps.join(", ")}`
23780
+ );
22665
23781
  }
22666
- const rawId = match?.[1];
22667
- if (!rawId) {
22668
- return null;
23782
+ const finalStep = resolveFinalStep(manifest.steps, manifest.finalStepId);
23783
+ if (!finalStep) {
23784
+ return validationError("steps", "No steps found in validation manifest");
22669
23785
  }
22670
- let decodedId;
22671
- try {
22672
- decodedId = decodeURIComponent(rawId);
22673
- } catch {
22674
- return null;
23786
+ const { artifact, error: artifactError } = await readFinalArtifact(
23787
+ sandboxDir,
23788
+ finalStep.output
23789
+ );
23790
+ if (artifactError) {
23791
+ return artifactError;
22675
23792
  }
22676
- if (decodedId.includes("\0")) {
22677
- return null;
23793
+ return {
23794
+ success: true,
23795
+ data: {
23796
+ status: "success",
23797
+ sandboxId: manifest.sandboxId ?? sandboxDir.split("/").pop(),
23798
+ workflowId: manifest.workflow,
23799
+ artifact
23800
+ }
23801
+ };
23802
+ }
23803
+ async function extractSandboxResult(options) {
23804
+ const loopliaHome = getLoopliaPluginPath();
23805
+ const sandboxRoot = options?.sandboxRoot ?? join42(loopliaHome, "sandbox");
23806
+ const sandboxId = options?.sandboxId;
23807
+ const sandboxDir = await resolveSandboxDir(sandboxRoot, sandboxId);
23808
+ if (!sandboxDir) {
23809
+ const message = sandboxId ? `Sandbox not found: ${sandboxId}` : "No sandbox directories found";
23810
+ return validationError("sandbox", message);
22678
23811
  }
22679
- if (decodedId.includes("..") || decodedId.includes("/") || decodedId.includes("\\")) {
22680
- return null;
23812
+ const { manifest, error: readError } = await readValidationManifest(sandboxDir);
23813
+ if (!manifest) {
23814
+ return validationError("validation.json", readError ?? "Unknown error");
22681
23815
  }
22682
- if (!VALID_SANDBOX_ID_PATTERN.test(decodedId)) {
22683
- return null;
23816
+ if (isBuildManifest(manifest)) {
23817
+ return extractBuildResult(manifest, sandboxDir);
22684
23818
  }
22685
- return decodedId;
23819
+ return extractRunResult(manifest, sandboxDir);
22686
23820
  }
22687
- var extractContentIdFromPrompt = extractSandboxIdFromPrompt;
22688
23821
  function expandPath(path) {
22689
23822
  if (path.startsWith("~/") || path === "~") {
22690
23823
  const home = homedir32();
22691
23824
  if (!home) {
22692
23825
  throw new Error("Unable to determine home directory");
22693
23826
  }
22694
- const expanded = path === "~" ? home : join42(home, path.slice(2));
23827
+ const expanded = path === "~" ? home : join52(home, path.slice(2));
22695
23828
  return normalize(expanded);
22696
23829
  }
22697
23830
  if (isAbsolute(path)) {
@@ -22700,26 +23833,26 @@ function expandPath(path) {
22700
23833
  return normalize(resolve(path));
22701
23834
  }
22702
23835
  function getPluginPath() {
22703
- return join42(process.cwd(), "plugins", "looplia-writer");
23836
+ return join52(process.cwd(), "plugins", "looplia-writer");
22704
23837
  }
22705
23838
  function getPluginPaths2() {
22706
- const base = join42(process.cwd(), "plugins");
23839
+ const base = join52(process.cwd(), "plugins");
22707
23840
  return {
22708
- core: join42(base, "looplia-core"),
22709
- writer: join42(base, "looplia-writer")
23841
+ core: join52(base, "looplia-core"),
23842
+ writer: join52(base, "looplia-writer")
22710
23843
  };
22711
23844
  }
22712
23845
  async function checkRequiredFiles(workspaceDir) {
22713
23846
  const requiredPaths = [
22714
23847
  // Core structure
22715
- join42(workspaceDir, "CLAUDE.md"),
22716
- join42(workspaceDir, ".claude", "agents"),
22717
- join42(workspaceDir, ".claude", "skills"),
22718
- join42(workspaceDir, "workflows"),
23848
+ join52(workspaceDir, "CLAUDE.md"),
23849
+ join52(workspaceDir, ".claude", "agents"),
23850
+ join52(workspaceDir, ".claude", "skills"),
23851
+ join52(workspaceDir, "workflows"),
22719
23852
  // From looplia-core plugin
22720
- join42(workspaceDir, ".claude", "commands"),
22721
- join42(workspaceDir, ".claude", "skills", "workflow-executor"),
22722
- join42(workspaceDir, ".claude", "skills", "workflow-validator")
23853
+ join52(workspaceDir, ".claude", "commands"),
23854
+ join52(workspaceDir, ".claude", "skills", "workflow-executor"),
23855
+ join52(workspaceDir, ".claude", "skills", "workflow-validator")
22723
23856
  ];
22724
23857
  for (const path of requiredPaths) {
22725
23858
  if (!await pathExists(path)) {
@@ -22748,33 +23881,33 @@ async function createTestWorkspace(workspaceDir, force) {
22748
23881
  await rm22(workspaceDir, { recursive: true, force: true });
22749
23882
  }
22750
23883
  await mkdir22(workspaceDir, { recursive: true });
22751
- await mkdir22(join42(workspaceDir, ".claude", "agents"), { recursive: true });
22752
- await mkdir22(join42(workspaceDir, ".claude", "skills"), { recursive: true });
22753
- await mkdir22(join42(workspaceDir, "sandbox"), { recursive: true });
23884
+ await mkdir22(join52(workspaceDir, ".claude", "agents"), { recursive: true });
23885
+ await mkdir22(join52(workspaceDir, ".claude", "skills"), { recursive: true });
23886
+ await mkdir22(join52(workspaceDir, "sandbox"), { recursive: true });
22754
23887
  const plugins = getPluginPaths2();
22755
- const writerWorkflowsDir = join42(plugins.writer, "workflows");
23888
+ const writerWorkflowsDir = join52(plugins.writer, "workflows");
22756
23889
  if (await pathExists(writerWorkflowsDir)) {
22757
- await cp2(writerWorkflowsDir, join42(workspaceDir, "workflows"), {
23890
+ await cp2(writerWorkflowsDir, join52(workspaceDir, "workflows"), {
22758
23891
  recursive: true
22759
23892
  });
22760
23893
  } else {
22761
- await mkdir22(join42(workspaceDir, "workflows"), { recursive: true });
23894
+ await mkdir22(join52(workspaceDir, "workflows"), { recursive: true });
22762
23895
  }
22763
- const coreValidatorDir = join42(plugins.core, "skills", "workflow-validator");
23896
+ const coreValidatorDir = join52(plugins.core, "skills", "workflow-validator");
22764
23897
  if (await pathExists(coreValidatorDir)) {
22765
23898
  await cp2(
22766
23899
  coreValidatorDir,
22767
- join42(workspaceDir, ".claude", "skills", "workflow-validator"),
23900
+ join52(workspaceDir, ".claude", "skills", "workflow-validator"),
22768
23901
  { recursive: true }
22769
23902
  );
22770
23903
  }
22771
23904
  await writeFile22(
22772
- join42(workspaceDir, "CLAUDE.md"),
23905
+ join52(workspaceDir, "CLAUDE.md"),
22773
23906
  "# Test Workspace\n",
22774
23907
  "utf-8"
22775
23908
  );
22776
23909
  await writeFile22(
22777
- join42(workspaceDir, "user-profile.json"),
23910
+ join52(workspaceDir, "user-profile.json"),
22778
23911
  JSON.stringify(createDefaultProfile2(), null, 2),
22779
23912
  "utf-8"
22780
23913
  );
@@ -22785,63 +23918,63 @@ async function bootstrapFromPlugins(workspaceDir, plugins) {
22785
23918
  await rm22(workspaceDir, { recursive: true, force: true });
22786
23919
  }
22787
23920
  await mkdir22(workspaceDir, { recursive: true });
22788
- await mkdir22(join42(workspaceDir, ".claude"), { recursive: true });
22789
- await mkdir22(join42(workspaceDir, "sandbox"), { recursive: true });
22790
- const coreCommandsDir = join42(plugins.core, "commands");
23921
+ await mkdir22(join52(workspaceDir, ".claude"), { recursive: true });
23922
+ await mkdir22(join52(workspaceDir, "sandbox"), { recursive: true });
23923
+ const coreCommandsDir = join52(plugins.core, "commands");
22791
23924
  if (await pathExists(coreCommandsDir)) {
22792
- await cp2(coreCommandsDir, join42(workspaceDir, ".claude", "commands"), {
23925
+ await cp2(coreCommandsDir, join52(workspaceDir, ".claude", "commands"), {
22793
23926
  recursive: true
22794
23927
  });
22795
23928
  }
22796
- const coreSkillsDir = join42(plugins.core, "skills");
23929
+ const coreSkillsDir = join52(plugins.core, "skills");
22797
23930
  if (await pathExists(coreSkillsDir)) {
22798
- await cp2(coreSkillsDir, join42(workspaceDir, ".claude", "skills"), {
23931
+ await cp2(coreSkillsDir, join52(workspaceDir, ".claude", "skills"), {
22799
23932
  recursive: true
22800
23933
  });
22801
23934
  }
22802
- const coreHooksDir = join42(plugins.core, "hooks");
23935
+ const coreHooksDir = join52(plugins.core, "hooks");
22803
23936
  if (await pathExists(coreHooksDir)) {
22804
- await cp2(coreHooksDir, join42(workspaceDir, ".claude", "hooks"), {
23937
+ await cp2(coreHooksDir, join52(workspaceDir, ".claude", "hooks"), {
22805
23938
  recursive: true
22806
23939
  });
22807
23940
  }
22808
- const coreAgentsDir = join42(plugins.core, "agents");
23941
+ const coreAgentsDir = join52(plugins.core, "agents");
22809
23942
  if (await pathExists(coreAgentsDir)) {
22810
- await mkdir22(join42(workspaceDir, ".claude", "agents"), { recursive: true });
22811
- await cp2(coreAgentsDir, join42(workspaceDir, ".claude", "agents"), {
23943
+ await mkdir22(join52(workspaceDir, ".claude", "agents"), { recursive: true });
23944
+ await cp2(coreAgentsDir, join52(workspaceDir, ".claude", "agents"), {
22812
23945
  recursive: true
22813
23946
  });
22814
23947
  }
22815
- const coreScriptsDir = join42(plugins.core, "scripts");
23948
+ const coreScriptsDir = join52(plugins.core, "scripts");
22816
23949
  if (await pathExists(coreScriptsDir)) {
22817
- await cp2(coreScriptsDir, join42(workspaceDir, "scripts"), {
23950
+ await cp2(coreScriptsDir, join52(workspaceDir, "scripts"), {
22818
23951
  recursive: true
22819
23952
  });
22820
23953
  }
22821
- const coreClaudeMd = join42(plugins.core, "CLAUDE.md");
23954
+ const coreClaudeMd = join52(plugins.core, "CLAUDE.md");
22822
23955
  if (await pathExists(coreClaudeMd)) {
22823
- await cp2(coreClaudeMd, join42(workspaceDir, "CLAUDE.md"));
23956
+ await cp2(coreClaudeMd, join52(workspaceDir, "CLAUDE.md"));
22824
23957
  }
22825
- const writerAgentsDir = join42(plugins.writer, "agents");
23958
+ const writerAgentsDir = join52(plugins.writer, "agents");
22826
23959
  if (await pathExists(writerAgentsDir)) {
22827
- await cp2(writerAgentsDir, join42(workspaceDir, ".claude", "agents"), {
23960
+ await cp2(writerAgentsDir, join52(workspaceDir, ".claude", "agents"), {
22828
23961
  recursive: true
22829
23962
  });
22830
23963
  }
22831
- const writerSkillsDir = join42(plugins.writer, "skills");
23964
+ const writerSkillsDir = join52(plugins.writer, "skills");
22832
23965
  if (await pathExists(writerSkillsDir)) {
22833
- await cp2(writerSkillsDir, join42(workspaceDir, ".claude", "skills"), {
23966
+ await cp2(writerSkillsDir, join52(workspaceDir, ".claude", "skills"), {
22834
23967
  recursive: true
22835
23968
  });
22836
23969
  }
22837
- const writerWorkflowsDir = join42(plugins.writer, "workflows");
23970
+ const writerWorkflowsDir = join52(plugins.writer, "workflows");
22838
23971
  if (await pathExists(writerWorkflowsDir)) {
22839
- await cp2(writerWorkflowsDir, join42(workspaceDir, "workflows"), {
23972
+ await cp2(writerWorkflowsDir, join52(workspaceDir, "workflows"), {
22840
23973
  recursive: true
22841
23974
  });
22842
23975
  }
22843
23976
  await writeFile22(
22844
- join42(workspaceDir, "user-profile.json"),
23977
+ join52(workspaceDir, "user-profile.json"),
22845
23978
  JSON.stringify(createDefaultProfile2(), null, 2),
22846
23979
  "utf-8"
22847
23980
  );
@@ -22879,12 +24012,12 @@ function getWorkspacePath(baseDir) {
22879
24012
  return expandPath(baseDir ?? "~/.looplia");
22880
24013
  }
22881
24014
  async function readUserProfile(workspaceDir) {
22882
- const profilePath = join42(workspaceDir, "user-profile.json");
22883
- const content = await readFile22(profilePath, "utf-8");
24015
+ const profilePath = join52(workspaceDir, "user-profile.json");
24016
+ const content = await readFile3(profilePath, "utf-8");
22884
24017
  return JSON.parse(content);
22885
24018
  }
22886
24019
  async function writeUserProfile(workspaceDir, profile) {
22887
- const profilePath = join42(workspaceDir, "user-profile.json");
24020
+ const profilePath = join52(workspaceDir, "user-profile.json");
22888
24021
  await writeFile22(profilePath, JSON.stringify(profile, null, 2), "utf-8");
22889
24022
  }
22890
24023
  var workspaceCache = /* @__PURE__ */ new Map();
@@ -22902,7 +24035,7 @@ async function getOrInitWorkspace(baseDir, useFilesystemExtensions) {
22902
24035
  return workspace;
22903
24036
  }
22904
24037
  function getApiKey(config2) {
22905
- return config2?.apiKey ?? process.env.ANTHROPIC_API_KEY ?? process.env.CLAUDE_CODE_OAUTH_TOKEN;
24038
+ return config2?.apiKey ?? process.env.ANTHROPIC_API_KEY ?? process.env.ANTHROPIC_AUTH_TOKEN ?? process.env.CLAUDE_CODE_OAUTH_TOKEN;
22906
24039
  }
22907
24040
  async function initializeAndValidateApiKey(config2) {
22908
24041
  const settings = await readLoopliaSettings();
@@ -22912,7 +24045,7 @@ async function initializeAndValidateApiKey(config2) {
22912
24045
  const apiKey = getApiKey(config2);
22913
24046
  if (!apiKey) {
22914
24047
  throw new Error(
22915
- "API key is required. Set ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN environment variable"
24048
+ "API key is required. Set ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, or CLAUDE_CODE_OAUTH_TOKEN environment variable"
22916
24049
  );
22917
24050
  }
22918
24051
  return apiKey;
@@ -23392,7 +24525,7 @@ function truncate(str, maxLen) {
23392
24525
  }
23393
24526
  return `${str.slice(0, maxLen - 3)}...`;
23394
24527
  }
23395
- async function* executeAgenticQueryStreaming(prompt, jsonSchema, config2) {
24528
+ async function* executeAgenticQueryStreaming(prompt, _jsonSchema, config2) {
23396
24529
  const resolvedConfig = resolveConfig(config2);
23397
24530
  await initializeAndValidateApiKey(config2);
23398
24531
  const mainModel = getMainModel();
@@ -23464,8 +24597,15 @@ When processing --file arguments or user file paths, resolve them against the Us
23464
24597
  "WebSearch",
23465
24598
  "WebFetch"
23466
24599
  ],
23467
- outputFormat: { type: "json_schema", schema: jsonSchema }
24600
+ // v0.7.2: Removed outputFormat to support non-Anthropic models
24601
+ // Final results are now extracted from sandbox output files via extractSandboxResult()
23468
24602
  // v0.6.9: No custom agents - using built-in general-purpose for workflow steps
24603
+ // v0.7.4: Pass runHooks from config if provided (workflow protection)
24604
+ // Only the run command passes hooks - other SDK usage doesn't need them
24605
+ ...config2?.runHooks && { hooks: config2.runHooks },
24606
+ // v0.7.5: Pass buildHooks from config if provided (build validation)
24607
+ // Only the build command passes hooks - validates workflow file structure
24608
+ ...config2?.buildHooks && { hooks: config2.buildHooks }
23469
24609
  }
23470
24610
  });
23471
24611
  let finalResult;
@@ -23489,6 +24629,12 @@ When processing --file arguments or user file paths, resolve them against the Us
23489
24629
  }
23490
24630
  logger.close();
23491
24631
  yield progressTracker.onComplete();
24632
+ if (!finalResult) {
24633
+ finalResult = await extractSandboxResult({
24634
+ sandboxId: process.env.LOOPLIA_SANDBOX_ID,
24635
+ sandboxRoot: process.env.LOOPLIA_SANDBOX_ROOT ?? join6(workspace, "sandbox")
24636
+ });
24637
+ }
23492
24638
  return finalResult ?? {
23493
24639
  success: false,
23494
24640
  error: { type: "unknown", message: "No result received" }
@@ -23633,6 +24779,519 @@ function createClaudeAgentExecutor(config2) {
23633
24779
  }
23634
24780
  };
23635
24781
  }
24782
+ var DEBUG = process.env.LOOPLIA_DEBUG === "1";
24783
+ function isWorkflowFile(filePath) {
24784
+ if (!filePath) {
24785
+ return false;
24786
+ }
24787
+ return filePath.includes("/workflows/") && filePath.endsWith(".md");
24788
+ }
24789
+ function resolveSandboxRoot(context) {
24790
+ return context?.sandboxRoot ?? join7(getLoopliaPluginPath(), "sandbox");
24791
+ }
24792
+ async function findMostRecentBuildSandbox(sandboxRoot) {
24793
+ try {
24794
+ const entries = await readdir22(sandboxRoot, { withFileTypes: true });
24795
+ const dirs = entries.filter(
24796
+ (e) => e.isDirectory() && e.name.startsWith("build-")
24797
+ );
24798
+ if (dirs.length === 0) {
24799
+ return;
24800
+ }
24801
+ const dirStats = await Promise.all(
24802
+ dirs.map(async (dir) => {
24803
+ const dirPath = join7(sandboxRoot, dir.name);
24804
+ const dirStat = await stat2(dirPath);
24805
+ return { path: dirPath, mtime: dirStat.mtime.getTime() };
24806
+ })
24807
+ );
24808
+ dirStats.sort((a, b) => b.mtime - a.mtime);
24809
+ return dirStats[0]?.path;
24810
+ } catch {
24811
+ return;
24812
+ }
24813
+ }
24814
+ async function resolveSandboxDir2(context) {
24815
+ const sandboxRoot = resolveSandboxRoot(context);
24816
+ if (context?.sandboxId) {
24817
+ return join7(sandboxRoot, context.sandboxId);
24818
+ }
24819
+ return await findMostRecentBuildSandbox(sandboxRoot);
24820
+ }
24821
+ async function readBuildValidation(context) {
24822
+ const sandboxDir = await resolveSandboxDir2(context);
24823
+ if (!sandboxDir) {
24824
+ if (DEBUG) {
24825
+ console.error("[build-hooks] readBuildValidation: no sandbox dir found");
24826
+ }
24827
+ return;
24828
+ }
24829
+ const validationPath = join7(sandboxDir, "validation.json");
24830
+ try {
24831
+ const content = await readFile4(validationPath, "utf-8");
24832
+ const manifest = JSON.parse(content);
24833
+ if (manifest.type === "build") {
24834
+ return manifest;
24835
+ }
24836
+ return;
24837
+ } catch {
24838
+ return;
24839
+ }
24840
+ }
24841
+ async function updateBuildValidation(context, updates) {
24842
+ const sandboxDir = await resolveSandboxDir2(context);
24843
+ if (!sandboxDir) {
24844
+ if (DEBUG) {
24845
+ console.error(
24846
+ "[build-hooks] updateBuildValidation: no sandbox dir found"
24847
+ );
24848
+ }
24849
+ return;
24850
+ }
24851
+ const validationPath = join7(sandboxDir, "validation.json");
24852
+ try {
24853
+ const content = await readFile4(validationPath, "utf-8");
24854
+ const manifest = JSON.parse(content);
24855
+ const updated = {
24856
+ ...manifest,
24857
+ ...updates
24858
+ };
24859
+ const tempPath = `${validationPath}.tmp`;
24860
+ await writeFile3(tempPath, JSON.stringify(updated, null, 2), "utf-8");
24861
+ await rename2(tempPath, validationPath);
24862
+ if (DEBUG) {
24863
+ console.error(
24864
+ `[build-hooks] updateBuildValidation: updated ${validationPath}`
24865
+ );
24866
+ }
24867
+ } catch (error2) {
24868
+ if (DEBUG) {
24869
+ console.error("[build-hooks] updateBuildValidation error:", error2);
24870
+ }
24871
+ }
24872
+ }
24873
+ function createBuildValidateHook(context) {
24874
+ return async (input) => {
24875
+ if (DEBUG) {
24876
+ console.error(
24877
+ "[build-hooks] PostToolUse fired:",
24878
+ JSON.stringify(input).slice(0, 200)
24879
+ );
24880
+ }
24881
+ const hookInput = input;
24882
+ const filePath = hookInput.tool_input?.file_path;
24883
+ const content = hookInput.tool_input?.content;
24884
+ if (DEBUG) {
24885
+ console.error("[build-hooks] file_path:", filePath);
24886
+ console.error("[build-hooks] isWorkflowFile:", isWorkflowFile(filePath));
24887
+ }
24888
+ if (!isWorkflowFile(filePath)) {
24889
+ return {};
24890
+ }
24891
+ if (!content) {
24892
+ return {
24893
+ decision: "block",
24894
+ reason: "Workflow validation failed: No content provided. Please write the workflow content to the file."
24895
+ };
24896
+ }
24897
+ try {
24898
+ const parsed = parseWorkflow(content);
24899
+ await updateBuildValidation(context ?? {}, {
24900
+ workflowValidated: true,
24901
+ workflowPath: filePath,
24902
+ workflowName: parsed.definition.name,
24903
+ status: "validated"
24904
+ });
24905
+ console.error(`\u2713 Workflow validated: ${parsed.definition.name}`);
24906
+ return {};
24907
+ } catch (error2) {
24908
+ const errorMessage = error2 instanceof Error ? error2.message : String(error2);
24909
+ await updateBuildValidation(context ?? {}, {
24910
+ workflowValidated: false,
24911
+ status: "failed",
24912
+ error: errorMessage
24913
+ });
24914
+ return {
24915
+ decision: "block",
24916
+ reason: `Workflow validation failed: ${errorMessage}
24917
+
24918
+ Please fix the workflow file and try writing it again. Ensure the workflow has valid YAML frontmatter with 'name', 'description', and 'steps' fields.`
24919
+ };
24920
+ }
24921
+ };
24922
+ }
24923
+ function createBuildStopGuardHook(context) {
24924
+ return async (input) => {
24925
+ if (DEBUG) {
24926
+ console.error(
24927
+ "[build-hooks] Stop/SubagentStop fired:",
24928
+ JSON.stringify(input).slice(0, 200)
24929
+ );
24930
+ }
24931
+ const hookInput = input;
24932
+ if (hookInput.stop_hook_active === true) {
24933
+ return {};
24934
+ }
24935
+ const validation = await readBuildValidation(context);
24936
+ if (DEBUG) {
24937
+ console.error("[build-hooks] validation:", JSON.stringify(validation));
24938
+ }
24939
+ if (!validation) {
24940
+ return {};
24941
+ }
24942
+ if (!validation.workflowValidated) {
24943
+ return {
24944
+ decision: "block",
24945
+ reason: "Build incomplete. The workflow file has not been validated yet. Please write the workflow to ~/.looplia/workflows/ with valid YAML frontmatter including 'name', 'description', and 'steps' fields. Do not stop until the workflow is successfully created."
24946
+ };
24947
+ }
24948
+ return {};
24949
+ };
24950
+ }
24951
+ function createBuildHooks() {
24952
+ const context = {
24953
+ sandboxId: process.env.LOOPLIA_SANDBOX_ID,
24954
+ sandboxRoot: process.env.LOOPLIA_SANDBOX_ROOT
24955
+ };
24956
+ return {
24957
+ Stop: [{ hooks: [createBuildStopGuardHook(context)] }],
24958
+ SubagentStop: [{ hooks: [createBuildStopGuardHook(context)] }],
24959
+ PostToolUse: [
24960
+ { matcher: "Write", hooks: [createBuildValidateHook(context)] }
24961
+ ]
24962
+ };
24963
+ }
24964
+ var JSON_EXTENSION_REGEX = /\.json$/;
24965
+ var LOCK_TIMEOUT_MS = 3e4;
24966
+ var LOCK_RETRY_DELAY_MS2 = 200;
24967
+ async function findMostRecentSandbox2(sandboxRoot) {
24968
+ try {
24969
+ const entries = await readdir32(sandboxRoot, { withFileTypes: true });
24970
+ const dirs = entries.filter(
24971
+ (e) => e.isDirectory() && !e.name.startsWith(".")
24972
+ );
24973
+ if (dirs.length === 0) {
24974
+ return;
24975
+ }
24976
+ const dirStats = await Promise.all(
24977
+ dirs.map(async (dir) => {
24978
+ const dirPath = join8(sandboxRoot, dir.name);
24979
+ const dirStat = await stat3(dirPath);
24980
+ return { path: dirPath, mtime: dirStat.mtime.getTime() };
24981
+ })
24982
+ );
24983
+ dirStats.sort((a, b) => b.mtime - a.mtime);
24984
+ return dirStats[0]?.path;
24985
+ } catch {
24986
+ return;
24987
+ }
24988
+ }
24989
+ async function readValidationManifest2(sandboxDir) {
24990
+ const validationPath = join8(sandboxDir, "validation.json");
24991
+ try {
24992
+ const content = await readFile5(validationPath, "utf-8");
24993
+ return JSON.parse(content);
24994
+ } catch {
24995
+ return;
24996
+ }
24997
+ }
24998
+ function resolveSandboxRoot2(context) {
24999
+ return context?.sandboxRoot ?? join8(getLoopliaPluginPath(), "sandbox");
25000
+ }
25001
+ async function resolveSandboxDir3(context) {
25002
+ const sandboxRoot = resolveSandboxRoot2(context);
25003
+ if (context?.sandboxId) {
25004
+ return join8(sandboxRoot, context.sandboxId);
25005
+ }
25006
+ return await findMostRecentSandbox2(sandboxRoot);
25007
+ }
25008
+ function isRecord(value) {
25009
+ return typeof value === "object" && value !== null;
25010
+ }
25011
+ function getSandboxArtifactInfo(filePath) {
25012
+ if (!filePath) {
25013
+ return;
25014
+ }
25015
+ if (!(filePath.includes("/sandbox/") && filePath.includes("/outputs/"))) {
25016
+ return;
25017
+ }
25018
+ const outputsIndex = filePath.lastIndexOf("/outputs/");
25019
+ if (outputsIndex < 0) {
25020
+ return;
25021
+ }
25022
+ const sandboxDir = filePath.slice(0, outputsIndex);
25023
+ const filename = filePath.slice(outputsIndex + "/outputs/".length);
25024
+ const artifact = filename.replace(JSON_EXTENSION_REGEX, "");
25025
+ return { sandboxDir, artifact };
25026
+ }
25027
+ async function readArtifactData(filePath, artifact) {
25028
+ try {
25029
+ const content = await readFile5(filePath, "utf-8");
25030
+ return JSON.parse(content);
25031
+ } catch {
25032
+ console.error(`Validation failed for ${artifact}: Invalid JSON`);
25033
+ return;
25034
+ }
25035
+ }
25036
+ function validateWorkflowArtifact(data, criteria) {
25037
+ const checks = [];
25038
+ if (criteria.required_fields) {
25039
+ checks.push(...checkRequiredFields(data, criteria.required_fields));
25040
+ }
25041
+ if (criteria.min_quotes !== void 0) {
25042
+ checks.push(checkMinQuotes(data, criteria.min_quotes));
25043
+ }
25044
+ if (criteria.min_key_points !== void 0) {
25045
+ checks.push(checkMinKeyPoints(data, criteria.min_key_points));
25046
+ }
25047
+ if (criteria.min_outline_sections !== void 0) {
25048
+ checks.push(checkMinOutlineSections(data, criteria.min_outline_sections));
25049
+ }
25050
+ if (criteria.has_hooks === true) {
25051
+ checks.push(checkHasHooks(data));
25052
+ }
25053
+ const passed = checks.every((check2) => check2.passed);
25054
+ return { passed, checks };
25055
+ }
25056
+ function checkRequiredFields(data, fields) {
25057
+ return fields.map((field) => {
25058
+ const exists = hasField(data, field);
25059
+ return {
25060
+ name: `has_${field}`,
25061
+ passed: exists,
25062
+ message: exists ? "OK" : `Missing required field: ${field}`
25063
+ };
25064
+ });
25065
+ }
25066
+ function checkMinQuotes(data, minQuotes) {
25067
+ const quotes = getArrayLength(data, "importantQuotes");
25068
+ const passed = quotes >= minQuotes;
25069
+ return {
25070
+ name: "min_quotes",
25071
+ passed,
25072
+ message: passed ? `Found ${quotes} quotes (min: ${minQuotes})` : `Found ${quotes} quotes, need at least ${minQuotes}`
25073
+ };
25074
+ }
25075
+ function checkMinKeyPoints(data, minPoints) {
25076
+ const bullets = getArrayLength(data, "bullets");
25077
+ const keyPoints = getArrayLength(data, "keyPoints");
25078
+ const count = Math.max(bullets, keyPoints);
25079
+ const passed = count >= minPoints;
25080
+ return {
25081
+ name: "min_key_points",
25082
+ passed,
25083
+ message: passed ? `Found ${count} key points (min: ${minPoints})` : `Found ${count} key points, need at least ${minPoints}`
25084
+ };
25085
+ }
25086
+ function checkMinOutlineSections(data, minSections) {
25087
+ const sections = getArrayLength(data, "suggestedOutline");
25088
+ const passed = sections >= minSections;
25089
+ return {
25090
+ name: "min_outline_sections",
25091
+ passed,
25092
+ message: passed ? `Found ${sections} outline sections (min: ${minSections})` : `Found ${sections} sections, need at least ${minSections}`
25093
+ };
25094
+ }
25095
+ function checkHasHooks(data) {
25096
+ const hooksCount = getHooksCount(data);
25097
+ const passed = hooksCount > 0;
25098
+ return {
25099
+ name: "has_hooks",
25100
+ passed,
25101
+ message: passed ? `Found ${hooksCount} hooks` : "No hooks found in artifact"
25102
+ };
25103
+ }
25104
+ function getHooksCount(data) {
25105
+ const ideas = data.ideas;
25106
+ if (ideas && Array.isArray(ideas.hooks)) {
25107
+ return ideas.hooks.length;
25108
+ }
25109
+ if (Array.isArray(data.hooks)) {
25110
+ return data.hooks.length;
25111
+ }
25112
+ return 0;
25113
+ }
25114
+ function hasField(data, field) {
25115
+ const parts = field.split(".");
25116
+ let current = data;
25117
+ for (const part of parts) {
25118
+ if (current === null || typeof current !== "object") {
25119
+ return false;
25120
+ }
25121
+ current = current[part];
25122
+ }
25123
+ return current !== void 0 && current !== null;
25124
+ }
25125
+ function getArrayLength(data, field) {
25126
+ const value = data[field];
25127
+ return Array.isArray(value) ? value.length : 0;
25128
+ }
25129
+ function validateArtifactData(artifact, artifactData, criteria) {
25130
+ if (criteria === void 0 || criteria === null) {
25131
+ return true;
25132
+ }
25133
+ if (!isRecord(criteria)) {
25134
+ console.error(`Validation failed for ${artifact}: Invalid criteria`);
25135
+ return false;
25136
+ }
25137
+ const result = validateWorkflowArtifact(
25138
+ artifactData,
25139
+ criteria
25140
+ );
25141
+ if (!result.passed) {
25142
+ console.error(`Semantic validation failed for ${artifact}:`);
25143
+ console.error(JSON.stringify(result, null, 2));
25144
+ }
25145
+ return result.passed;
25146
+ }
25147
+ async function sleep2(ms) {
25148
+ await new Promise((resolve2) => setTimeout(resolve2, ms));
25149
+ }
25150
+ async function acquireLock(lockPath, timeoutMs) {
25151
+ const start = Date.now();
25152
+ while (Date.now() - start < timeoutMs) {
25153
+ try {
25154
+ const handle = await open2(lockPath, "wx");
25155
+ return {
25156
+ release: async () => {
25157
+ await handle.close();
25158
+ try {
25159
+ await unlink(lockPath);
25160
+ } catch {
25161
+ }
25162
+ }
25163
+ };
25164
+ } catch (error2) {
25165
+ if (error2 instanceof Error && "code" in error2 && error2.code === "EEXIST") {
25166
+ await sleep2(LOCK_RETRY_DELAY_MS2);
25167
+ continue;
25168
+ }
25169
+ throw error2;
25170
+ }
25171
+ }
25172
+ return;
25173
+ }
25174
+ async function writeValidationManifest(validationPath, manifest) {
25175
+ const tempPath = `${validationPath}.tmp`;
25176
+ await writeFile4(tempPath, JSON.stringify(manifest, null, 2), "utf-8");
25177
+ await rename3(tempPath, validationPath);
25178
+ }
25179
+ async function findMissingOutputs(sandboxDir, steps) {
25180
+ const missing = [];
25181
+ for (const [stepId, stepState] of Object.entries(steps)) {
25182
+ const outputPath = stepState.output;
25183
+ if (!outputPath) {
25184
+ continue;
25185
+ }
25186
+ const fullPath = join8(sandboxDir, outputPath);
25187
+ try {
25188
+ await stat3(fullPath);
25189
+ } catch {
25190
+ missing.push(stepId);
25191
+ }
25192
+ }
25193
+ return missing;
25194
+ }
25195
+ function findPendingSteps(steps) {
25196
+ return Object.entries(steps).filter(([, stepState]) => !stepState.validated).map(([stepId]) => stepId);
25197
+ }
25198
+ function createStopGuardHook(context) {
25199
+ return async (input) => {
25200
+ const hookInput = input;
25201
+ if (hookInput.stop_hook_active === true) {
25202
+ return {};
25203
+ }
25204
+ const sandboxDir = await resolveSandboxDir3(context);
25205
+ if (!sandboxDir) {
25206
+ return {};
25207
+ }
25208
+ const manifest = await readValidationManifest2(sandboxDir);
25209
+ if (!manifest) {
25210
+ return {};
25211
+ }
25212
+ const missingOutputs = await findMissingOutputs(sandboxDir, manifest.steps);
25213
+ if (missingOutputs.length > 0) {
25214
+ const stepsList = missingOutputs.join(", ");
25215
+ return {
25216
+ decision: "block",
25217
+ reason: `Your workflow is not complete. You still need to create output files for these steps: ${stepsList}. Please continue working on the workflow by using the Write tool to create the required JSON files at the paths specified in validation.json for each incomplete step. Do not stop until all workflow steps have their output files created.`
25218
+ };
25219
+ }
25220
+ const pendingSteps = findPendingSteps(manifest.steps);
25221
+ if (pendingSteps.length > 0) {
25222
+ const stepsList = pendingSteps.join(", ");
25223
+ return {
25224
+ decision: "block",
25225
+ reason: `Your workflow is not complete. The following steps need validation: ${stepsList}. The output files exist but have not been validated yet. Please re-write these output files using the Write tool to trigger validation. Do not stop until all workflow steps are validated.`
25226
+ };
25227
+ }
25228
+ return {};
25229
+ };
25230
+ }
25231
+ var stopGuardHook = createStopGuardHook();
25232
+ function createPostWriteValidateHook() {
25233
+ return async (input) => {
25234
+ const hookInput = input;
25235
+ const filePath = hookInput.tool_input?.file_path;
25236
+ const artifactInfo = getSandboxArtifactInfo(filePath);
25237
+ if (!(artifactInfo && filePath)) {
25238
+ return {};
25239
+ }
25240
+ const artifactData = await readArtifactData(
25241
+ filePath,
25242
+ artifactInfo.artifact
25243
+ );
25244
+ if (!artifactData) {
25245
+ return {};
25246
+ }
25247
+ const validationPath = join8(artifactInfo.sandboxDir, "validation.json");
25248
+ const lock = await acquireLock(`${validationPath}.lock`, LOCK_TIMEOUT_MS);
25249
+ if (!lock) {
25250
+ console.error("Failed to acquire lock on validation.json");
25251
+ return {};
25252
+ }
25253
+ try {
25254
+ const manifest = await readValidationManifest2(artifactInfo.sandboxDir);
25255
+ if (!manifest) {
25256
+ return {};
25257
+ }
25258
+ const stepState = manifest.steps[artifactInfo.artifact];
25259
+ if (!stepState) {
25260
+ return {};
25261
+ }
25262
+ const isValid3 = validateArtifactData(
25263
+ artifactInfo.artifact,
25264
+ artifactData,
25265
+ stepState.validate
25266
+ );
25267
+ if (!isValid3) {
25268
+ return {};
25269
+ }
25270
+ manifest.steps[artifactInfo.artifact] = {
25271
+ ...stepState,
25272
+ validated: true,
25273
+ validatedAt: (/* @__PURE__ */ new Date()).toISOString()
25274
+ };
25275
+ await writeValidationManifest(validationPath, manifest);
25276
+ console.error(`\u2713 Validated: ${artifactInfo.artifact}.json`);
25277
+ } finally {
25278
+ await lock.release();
25279
+ }
25280
+ return {};
25281
+ };
25282
+ }
25283
+ var postWriteValidateHook = createPostWriteValidateHook();
25284
+ function createWorkflowHooks() {
25285
+ const context = {
25286
+ sandboxId: process.env.LOOPLIA_SANDBOX_ID,
25287
+ sandboxRoot: process.env.LOOPLIA_SANDBOX_ROOT
25288
+ };
25289
+ return {
25290
+ Stop: [{ hooks: [createStopGuardHook(context)] }],
25291
+ SubagentStop: [{ hooks: [createStopGuardHook(context)] }],
25292
+ PostToolUse: [{ matcher: "Write", hooks: [createPostWriteValidateHook()] }]
25293
+ };
25294
+ }
23636
25295
  var SUMMARIZE_SYSTEM_PROMPT = `You are an expert content analyst specializing in summarization and content intelligence.
23637
25296
 
23638
25297
  Your expertise includes:
@@ -23663,7 +25322,7 @@ The content-analyzer agent will:
23663
25322
  3. Use content-documenter skill for structured documentation
23664
25323
  4. Write results to sandbox/${content.id}/outputs/summary.json`;
23665
25324
  }
23666
- async function* executeInteractiveQueryStreaming(prompt, jsonSchema, config2, questionCallback) {
25325
+ async function* executeInteractiveQueryStreaming(prompt, _jsonSchema, config2, questionCallback) {
23667
25326
  const resolvedConfig = resolveConfig(config2);
23668
25327
  await initializeAndValidateApiKey(config2);
23669
25328
  const mainModel = getMainModel();
@@ -23762,7 +25421,11 @@ When processing --file arguments or user file paths, resolve them against the Us
23762
25421
  "AskUserQuestion"
23763
25422
  // Only in interactive mode
23764
25423
  ],
23765
- outputFormat: { type: "json_schema", schema: jsonSchema }
25424
+ // v0.7.5: Pass hooks from config if provided (build validation)
25425
+ ...resolvedConfig?.runHooks && { hooks: resolvedConfig.runHooks },
25426
+ ...resolvedConfig?.buildHooks && { hooks: resolvedConfig.buildHooks }
25427
+ // v0.7.2: Removed outputFormat to support non-Anthropic models
25428
+ // Final results are now extracted from sandbox output files via extractSandboxResult()
23766
25429
  }
23767
25430
  });
23768
25431
  let finalResult;
@@ -23786,6 +25449,12 @@ When processing --file arguments or user file paths, resolve them against the Us
23786
25449
  }
23787
25450
  logger.close();
23788
25451
  yield progressTracker.onComplete();
25452
+ if (!finalResult) {
25453
+ finalResult = await extractSandboxResult({
25454
+ sandboxId: process.env.LOOPLIA_SANDBOX_ID,
25455
+ sandboxRoot: process.env.LOOPLIA_SANDBOX_ROOT ?? join9(workspace, "sandbox")
25456
+ });
25457
+ }
23789
25458
  return finalResult ?? {
23790
25459
  success: false,
23791
25460
  error: { type: "unknown", message: "No result received" }
@@ -24018,109 +25687,12 @@ export {
24018
25687
  copyPlugins,
24019
25688
  downloadRemotePlugins,
24020
25689
  isLoopliaInitialized,
24021
- util,
24022
- objectUtil,
24023
- ZodParsedType,
24024
- getParsedType,
24025
- ZodIssueCode,
24026
- quotelessJson,
24027
- ZodError,
24028
- en_default,
24029
- setErrorMap,
24030
- getErrorMap,
24031
- makeIssue,
24032
- EMPTY_PATH,
24033
- addIssueToContext,
24034
- ParseStatus,
24035
- INVALID,
24036
- DIRTY,
24037
- OK,
24038
- isAborted,
24039
- isDirty,
24040
- isValid,
24041
- isAsync,
24042
- ZodType,
24043
- datetimeRegex,
24044
- ZodString,
24045
- ZodNumber,
24046
- ZodBigInt,
24047
- ZodBoolean,
24048
- ZodDate,
24049
- ZodSymbol,
24050
- ZodUndefined,
24051
- ZodNull,
24052
- ZodAny,
24053
- ZodUnknown,
24054
- ZodNever,
24055
- ZodVoid,
24056
- ZodArray,
24057
- ZodObject,
24058
- ZodUnion,
24059
- ZodDiscriminatedUnion,
24060
- ZodIntersection,
24061
- ZodTuple,
24062
- ZodRecord,
24063
- ZodMap,
24064
- ZodSet,
24065
- ZodFunction,
24066
- ZodLazy,
24067
- ZodLiteral,
24068
- ZodEnum,
24069
- ZodNativeEnum,
24070
- ZodPromise,
24071
- ZodEffects,
24072
- ZodOptional,
24073
- ZodNullable,
24074
- ZodDefault,
24075
- ZodCatch,
24076
- ZodNaN,
24077
- BRAND,
24078
- ZodBranded,
24079
- ZodPipeline,
24080
- ZodReadonly,
24081
- custom,
24082
- late,
24083
- ZodFirstPartyTypeKind,
24084
- instanceOfType,
24085
- stringType,
24086
- numberType,
24087
- nanType,
24088
- bigIntType,
24089
- booleanType,
24090
- dateType,
24091
- symbolType,
24092
- undefinedType,
24093
- nullType,
24094
- anyType,
24095
- unknownType,
24096
- neverType,
24097
- voidType,
24098
- arrayType,
24099
- objectType,
24100
- strictObjectType,
24101
- unionType,
24102
- discriminatedUnionType,
24103
- intersectionType,
24104
- tupleType,
24105
- recordType,
24106
- mapType,
24107
- setType,
24108
- functionType,
24109
- lazyType,
24110
- literalType,
24111
- enumType,
24112
- nativeEnumType,
24113
- promiseType,
24114
- effectsType,
24115
- optionalType,
24116
- nullableType,
24117
- preprocessType,
24118
- pipelineType,
24119
- ostring,
24120
- onumber,
24121
- oboolean,
24122
- coerce,
24123
- NEVER,
25690
+ parseWorkflow,
25691
+ generateValidationManifest,
25692
+ getFinalStep,
25693
+ isInputlessWorkflow,
25694
+ extractWorkflowSkills,
25695
+ validateUserProfile,
24124
25696
  findClaudeCodePath,
24125
25697
  isClaudeCodeInstalled,
24126
25698
  clearClaudeCodePathCache,
@@ -24147,6 +25719,8 @@ export {
24147
25719
  writeUserProfile,
24148
25720
  executeAgenticQueryStreaming,
24149
25721
  createClaudeAgentExecutor,
25722
+ createBuildHooks,
25723
+ createWorkflowHooks,
24150
25724
  SUMMARIZE_SYSTEM_PROMPT,
24151
25725
  buildSummarizePrompt,
24152
25726
  executeInteractiveQueryStreaming,