@looplia/looplia-cli 0.7.2 → 0.7.4

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.
@@ -7,7 +7,7 @@ import {
7
7
  init_esm_shims
8
8
  } from "./chunk-Y55L47HC.js";
9
9
 
10
- // ../../packages/provider/dist/chunk-Q57VKNQX.js
10
+ // ../../packages/provider/dist/chunk-DRG2TRPF.js
11
11
  init_esm_shims();
12
12
  import { createHash } from "crypto";
13
13
  import {
@@ -175,7 +175,7 @@ async function copyPlugins(targetDir, sourcePath) {
175
175
  );
176
176
  const { initializeRegistry, compileRegistry } = await import("./compiler-4B63UTUP-VE77VSJ2.js");
177
177
  await initializeRegistry();
178
- const { syncRegistrySources } = await import("./sync-MFM46YAB-SSBMNJ7D.js");
178
+ const { syncRegistrySources } = await import("./sync-E5PGFGNI-IGGJR7IL.js");
179
179
  const syncResults = await syncRegistrySources({ showProgress: true });
180
180
  for (const result of syncResults) {
181
181
  if (result.status === "failed") {
@@ -293,7 +293,7 @@ async function getPluginPaths() {
293
293
  return await getProdPluginPaths();
294
294
  }
295
295
 
296
- // ../../packages/provider/dist/chunk-OG5PVVUV.js
296
+ // ../../packages/provider/dist/chunk-KZZIM7FY.js
297
297
  init_esm_shims();
298
298
  import { execSync } from "child_process";
299
299
  import { existsSync as existsSync3 } from "fs";
@@ -5592,6 +5592,9 @@ var zodToJsonSchema = (schema, options) => {
5592
5592
  return combined;
5593
5593
  };
5594
5594
 
5595
+ // ../../packages/provider/dist/chunk-KZZIM7FY.js
5596
+ import { join as join6 } from "path";
5597
+
5595
5598
  // ../../node_modules/@anthropic-ai/claude-agent-sdk/sdk.mjs
5596
5599
  init_esm_shims();
5597
5600
  import { join as join5 } from "path";
@@ -22127,12 +22130,25 @@ function query({
22127
22130
  return queryInstance;
22128
22131
  }
22129
22132
 
22130
- // ../../packages/provider/dist/chunk-OG5PVVUV.js
22133
+ // ../../packages/provider/dist/chunk-KZZIM7FY.js
22131
22134
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync3, writeFileSync } from "fs";
22132
22135
  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";
22136
+ import { access, readdir as readdir3, readFile as readFile22, stat } from "fs/promises";
22137
+ import { join as join42 } from "path";
22138
+ import { cp as cp2, mkdir as mkdir22, readFile as readFile3, rm as rm22, writeFile as writeFile22 } from "fs/promises";
22134
22139
  import { homedir as homedir32 } from "os";
22135
- import { isAbsolute, join as join42, normalize, resolve } from "path";
22140
+ import { isAbsolute, join as join52, normalize, resolve } from "path";
22141
+ import {
22142
+ open as open2,
22143
+ readdir as readdir22,
22144
+ readFile as readFile4,
22145
+ rename as rename2,
22146
+ stat as stat2,
22147
+ unlink,
22148
+ writeFile as writeFile3
22149
+ } from "fs/promises";
22150
+ import { join as join7 } from "path";
22151
+ import { join as join8 } from "path";
22136
22152
  var LINE_SPLIT_REGEX = /\r?\n/;
22137
22153
  function findSdkBundledCliPath() {
22138
22154
  try {
@@ -22226,11 +22242,42 @@ var PRESETS = {
22226
22242
  ANTHROPIC_CLAUDE_SONNET: {
22227
22243
  name: "Anthropic Claude Sonnet",
22228
22244
  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"
22245
+ mainModel: "claude-sonnet-4-5-20250929",
22246
+ executorModel: "claude-sonnet-4-5-20250929",
22247
+ haikuModel: "claude-sonnet-4-5-20250929",
22248
+ sonnetModel: "claude-sonnet-4-5-20250929",
22249
+ opusModel: "claude-sonnet-4-5-20250929"
22250
+ },
22251
+ // Claude Code Subscription (macOS Keychain)
22252
+ CLAUDE_CODE_SUBSCRIPTION_HAIKU: {
22253
+ name: "Claude Code Subscription (Haiku)",
22254
+ apiProvider: "anthropic",
22255
+ authTokenSource: "subscription",
22256
+ mainModel: "claude-haiku-4-5-20251001",
22257
+ executorModel: "claude-haiku-4-5-20251001",
22258
+ haikuModel: "claude-haiku-4-5-20251001",
22259
+ sonnetModel: "claude-haiku-4-5-20251001",
22260
+ opusModel: "claude-haiku-4-5-20251001"
22261
+ },
22262
+ CLAUDE_CODE_SUBSCRIPTION_SONNET: {
22263
+ name: "Claude Code Subscription (Sonnet)",
22264
+ apiProvider: "anthropic",
22265
+ authTokenSource: "subscription",
22266
+ mainModel: "claude-sonnet-4-5-20250929",
22267
+ executorModel: "claude-sonnet-4-5-20250929",
22268
+ haikuModel: "claude-sonnet-4-5-20250929",
22269
+ sonnetModel: "claude-sonnet-4-5-20250929",
22270
+ opusModel: "claude-sonnet-4-5-20250929"
22271
+ },
22272
+ CLAUDE_CODE_SUBSCRIPTION_OPUS: {
22273
+ name: "Claude Code Subscription (Opus)",
22274
+ apiProvider: "anthropic",
22275
+ authTokenSource: "subscription",
22276
+ mainModel: "claude-opus-4-5-20251101",
22277
+ executorModel: "claude-opus-4-5-20251101",
22278
+ haikuModel: "claude-opus-4-5-20251101",
22279
+ sonnetModel: "claude-opus-4-5-20251101",
22280
+ opusModel: "claude-opus-4-5-20251101"
22234
22281
  },
22235
22282
  // ZenMux Presets
22236
22283
  ZENMUX_ANTHROPIC_HAIKU45: {
@@ -22253,6 +22300,16 @@ var PRESETS = {
22253
22300
  sonnetModel: "z-ai/glm-4.7",
22254
22301
  opusModel: "z-ai/glm-4.7"
22255
22302
  },
22303
+ ZENMUX_ZAI_GLM47FLASHX: {
22304
+ name: "ZenMux GLM-4.7-FlashX",
22305
+ apiProvider: "zenmux",
22306
+ baseUrl: "https://zenmux.ai/api/anthropic",
22307
+ mainModel: "z-ai/glm-4.7-flashx",
22308
+ executorModel: "z-ai/glm-4.7-flashx",
22309
+ haikuModel: "z-ai/glm-4.7-flashx",
22310
+ sonnetModel: "z-ai/glm-4.7-flashx",
22311
+ opusModel: "z-ai/glm-4.7-flashx"
22312
+ },
22256
22313
  ZENMUX_MINIMAX_M21: {
22257
22314
  name: "ZenMux MiniMax-M2.1",
22258
22315
  apiProvider: "zenmux",
@@ -22372,6 +22429,48 @@ var PRESETS = {
22372
22429
  haikuModel: "openai/gpt-5.1-codex-mini",
22373
22430
  sonnetModel: "openai/gpt-5.1-codex-mini",
22374
22431
  opusModel: "openai/gpt-5.1-codex-mini"
22432
+ },
22433
+ // OpenRouter Presets
22434
+ OPENROUTER_PRESET: {
22435
+ name: "OpenRouter (User-Configured Preset)",
22436
+ apiProvider: "openrouter",
22437
+ baseUrl: "https://openrouter.ai/api",
22438
+ mainModel: "@preset/looplia-default",
22439
+ executorModel: "@preset/looplia-default",
22440
+ haikuModel: "@preset/looplia-default",
22441
+ sonnetModel: "@preset/looplia-default",
22442
+ opusModel: "@preset/looplia-default"
22443
+ },
22444
+ OPENROUTER_ZAI_GLM47FLASH: {
22445
+ name: "OpenRouter GLM-4.7-Flash",
22446
+ apiProvider: "openrouter",
22447
+ baseUrl: "https://openrouter.ai/api",
22448
+ mainModel: "z-ai/glm-4.7-flash",
22449
+ executorModel: "z-ai/glm-4.7-flash",
22450
+ haikuModel: "z-ai/glm-4.7-flash",
22451
+ sonnetModel: "z-ai/glm-4.7-flash",
22452
+ opusModel: "z-ai/glm-4.7-flash"
22453
+ },
22454
+ // Ollama Cloud Presets
22455
+ OLLAMA_GLM47_CLOUD: {
22456
+ name: "Ollama GLM-4.7 Cloud",
22457
+ apiProvider: "ollama",
22458
+ baseUrl: "http://localhost:11434",
22459
+ mainModel: "glm-4.7:cloud",
22460
+ executorModel: "glm-4.7:cloud",
22461
+ haikuModel: "glm-4.7:cloud",
22462
+ sonnetModel: "glm-4.7:cloud",
22463
+ opusModel: "glm-4.7:cloud"
22464
+ },
22465
+ OLLAMA_MINIMAX_M21_CLOUD: {
22466
+ name: "Ollama MiniMax-M2.1 Cloud",
22467
+ apiProvider: "ollama",
22468
+ baseUrl: "http://localhost:11434",
22469
+ mainModel: "minimax-m2.1:cloud",
22470
+ executorModel: "minimax-m2.1:cloud",
22471
+ haikuModel: "minimax-m2.1:cloud",
22472
+ sonnetModel: "minimax-m2.1:cloud",
22473
+ opusModel: "minimax-m2.1:cloud"
22375
22474
  }
22376
22475
  };
22377
22476
  var CONFIG_FILE = "looplia.setting.json";
@@ -22423,19 +22522,45 @@ function injectModelTierEnv(mainModel, executorModel) {
22423
22522
  setEnvIfNotSet("LOOPLIA_AGENT_MODEL_MAIN", mainModel);
22424
22523
  setEnvIfNotSet("LOOPLIA_AGENT_MODEL_EXECUTOR", executorModel);
22425
22524
  }
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;
22525
+ function injectSubscriptionAuth() {
22526
+ if (!process.env.CLAUDE_CODE_OAUTH_TOKEN) {
22527
+ console.warn(
22528
+ "Warning: CLAUDE_CODE_OAUTH_TOKEN not set. Set this environment variable to use Claude Code subscription auth."
22529
+ );
22530
+ }
22531
+ process.env.ANTHROPIC_API_KEY = void 0;
22532
+ }
22533
+ function injectNonAnthropicProviderEnv(apiProvider) {
22534
+ if (apiProvider.baseUrl && !process.env.ANTHROPIC_BASE_URL) {
22535
+ process.env.ANTHROPIC_BASE_URL = apiProvider.baseUrl;
22536
+ }
22537
+ const isOpenRouterEndpoint = apiProvider.type === "openrouter" || apiProvider.baseUrl?.includes("openrouter.ai");
22538
+ const isZenmuxEndpoint = apiProvider.type === "zenmux" || apiProvider.baseUrl?.includes("zenmux.ai");
22539
+ const isOllamaEndpoint = apiProvider.type === "ollama" || apiProvider.baseUrl?.includes("localhost:11434");
22540
+ if (apiProvider.authToken) {
22541
+ if (isOpenRouterEndpoint) {
22542
+ process.env.ANTHROPIC_AUTH_TOKEN = apiProvider.authToken;
22543
+ process.env.ANTHROPIC_API_KEY = void 0;
22433
22544
  } 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
- }
22545
+ process.env.ANTHROPIC_API_KEY = apiProvider.authToken;
22438
22546
  }
22547
+ return;
22548
+ }
22549
+ if (isZenmuxEndpoint && process.env.ZENMUX_API_KEY) {
22550
+ process.env.ANTHROPIC_API_KEY = process.env.ZENMUX_API_KEY;
22551
+ } else if (isOpenRouterEndpoint && process.env.OPENROUTER_API_KEY) {
22552
+ process.env.ANTHROPIC_AUTH_TOKEN = process.env.OPENROUTER_API_KEY;
22553
+ process.env.ANTHROPIC_API_KEY = void 0;
22554
+ } else if (isOllamaEndpoint) {
22555
+ process.env.ANTHROPIC_API_KEY = process.env.OLLAMA_API_KEY || "ollama";
22556
+ }
22557
+ }
22558
+ function injectLoopliaSettingsEnv(settings) {
22559
+ if (settings.apiProvider.authTokenSource === "subscription") {
22560
+ injectSubscriptionAuth();
22561
+ }
22562
+ if (settings.apiProvider.type !== "anthropic") {
22563
+ injectNonAnthropicProviderEnv(settings.apiProvider);
22439
22564
  }
22440
22565
  injectModelTierEnv(settings.agents.main, settings.agents.executor);
22441
22566
  }
@@ -22456,6 +22581,7 @@ function getSettingsDisplayInfo(settings) {
22456
22581
  preset: settings.preset,
22457
22582
  provider,
22458
22583
  authToken: settings.apiProvider.authToken,
22584
+ authTokenSource: settings.apiProvider.authTokenSource,
22459
22585
  agents: {
22460
22586
  main: settings.agents.main,
22461
22587
  executor: settings.agents.executor
@@ -22479,7 +22605,8 @@ function applyPreset(presetName, existingSettings) {
22479
22605
  apiProvider: {
22480
22606
  type: preset.apiProvider,
22481
22607
  baseUrl: preset.baseUrl,
22482
- authToken: existingSettings?.apiProvider.authToken
22608
+ authToken: existingSettings?.apiProvider.authToken,
22609
+ authTokenSource: preset.authTokenSource
22483
22610
  },
22484
22611
  agents: {
22485
22612
  main: preset.mainModel,
@@ -22498,17 +22625,19 @@ async function initializeCommandEnvironment(options = {}) {
22498
22625
  return { settings };
22499
22626
  }
22500
22627
  function validateApiKeyPresence() {
22501
- if (!(process.env.ANTHROPIC_API_KEY || process.env.CLAUDE_CODE_OAUTH_TOKEN)) {
22628
+ if (!(process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN || process.env.CLAUDE_CODE_OAUTH_TOKEN)) {
22502
22629
  console.error("Error: API key required");
22503
22630
  console.error("");
22504
22631
  console.error("Options:");
22505
22632
  console.error(" 1. Set ANTHROPIC_API_KEY environment variable");
22506
22633
  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");
22634
+ console.error(" 3. Set OPENROUTER_API_KEY with an OpenRouter preset");
22635
+ console.error(" 4. Configure via: looplia config provider preset <name>");
22636
+ console.error(" 5. Use --mock flag for testing without API");
22509
22637
  console.error("");
22510
22638
  console.error("Get your API key from: https://console.anthropic.com");
22511
22639
  console.error("Or use ZenMux at: https://zenmux.ai");
22640
+ console.error("Or use OpenRouter at: https://openrouter.ai");
22512
22641
  process.exit(1);
22513
22642
  }
22514
22643
  }
@@ -22685,13 +22814,186 @@ function extractSandboxIdFromPrompt(prompt) {
22685
22814
  return decodedId;
22686
22815
  }
22687
22816
  var extractContentIdFromPrompt = extractSandboxIdFromPrompt;
22817
+ var LOCK_MAX_RETRIES = 5;
22818
+ var LOCK_RETRY_DELAY_MS = 200;
22819
+ function isValidationManifest(value) {
22820
+ if (!value || typeof value !== "object") {
22821
+ return false;
22822
+ }
22823
+ const obj = value;
22824
+ return typeof obj.workflow === "string" && typeof obj.steps === "object" && obj.steps !== null;
22825
+ }
22826
+ async function sleep(ms) {
22827
+ await new Promise((resolve2) => setTimeout(resolve2, ms));
22828
+ }
22829
+ async function lockExists(lockPath) {
22830
+ try {
22831
+ await access(lockPath);
22832
+ return true;
22833
+ } catch {
22834
+ return false;
22835
+ }
22836
+ }
22837
+ function validationError(field, message) {
22838
+ return {
22839
+ success: false,
22840
+ error: { type: "validation_error", field, message }
22841
+ };
22842
+ }
22843
+ async function findMostRecentSandbox(sandboxRoot) {
22844
+ try {
22845
+ const entries = await readdir3(sandboxRoot, { withFileTypes: true });
22846
+ const dirs = entries.filter(
22847
+ (e) => e.isDirectory() && !e.name.startsWith(".")
22848
+ );
22849
+ if (dirs.length === 0) {
22850
+ return;
22851
+ }
22852
+ const dirStats = await Promise.all(
22853
+ dirs.map(async (dir) => {
22854
+ const dirPath = join42(sandboxRoot, dir.name);
22855
+ const dirStat = await stat(dirPath);
22856
+ return { path: dirPath, mtime: dirStat.mtime.getTime() };
22857
+ })
22858
+ );
22859
+ dirStats.sort((a, b) => b.mtime - a.mtime);
22860
+ return dirStats[0]?.path;
22861
+ } catch {
22862
+ return;
22863
+ }
22864
+ }
22865
+ function findFinalStep(steps) {
22866
+ const stepEntries = Object.entries(steps);
22867
+ if (stepEntries.length === 0) {
22868
+ return;
22869
+ }
22870
+ const lastEntry = stepEntries.at(-1);
22871
+ if (!lastEntry) {
22872
+ return;
22873
+ }
22874
+ return { id: lastEntry[0], output: lastEntry[1].output };
22875
+ }
22876
+ function resolveFinalStep(steps, finalStepId) {
22877
+ if (finalStepId) {
22878
+ const step = steps[finalStepId];
22879
+ if (step?.output) {
22880
+ return { id: finalStepId, output: step.output };
22881
+ }
22882
+ }
22883
+ return findFinalStep(steps);
22884
+ }
22885
+ function areAllStepsValidated(steps) {
22886
+ const pendingSteps = [];
22887
+ for (const [stepId, stepState] of Object.entries(steps)) {
22888
+ if (!stepState.validated) {
22889
+ pendingSteps.push(stepId);
22890
+ }
22891
+ }
22892
+ return {
22893
+ allValidated: pendingSteps.length === 0,
22894
+ pendingSteps
22895
+ };
22896
+ }
22897
+ function resolveSandboxDir(sandboxRoot, sandboxId) {
22898
+ if (sandboxId) {
22899
+ return join42(sandboxRoot, sandboxId);
22900
+ }
22901
+ return findMostRecentSandbox(sandboxRoot);
22902
+ }
22903
+ async function readValidationManifest(sandboxDir) {
22904
+ const validationPath = join42(sandboxDir, "validation.json");
22905
+ const lockPath = `${validationPath}.lock`;
22906
+ for (let attempt = 0; attempt < LOCK_MAX_RETRIES; attempt++) {
22907
+ if (await lockExists(lockPath)) {
22908
+ await sleep(LOCK_RETRY_DELAY_MS);
22909
+ continue;
22910
+ }
22911
+ try {
22912
+ const content = await readFile22(validationPath, "utf-8");
22913
+ const parsed = JSON.parse(content);
22914
+ if (!isValidationManifest(parsed)) {
22915
+ return { error: "Invalid validation manifest structure" };
22916
+ }
22917
+ return { manifest: parsed };
22918
+ } catch (error2) {
22919
+ const message = error2 instanceof Error ? error2.message : String(error2);
22920
+ return { error: `Failed to read validation.json: ${message}` };
22921
+ }
22922
+ }
22923
+ return { error: "Timeout waiting for validation.json lock to release" };
22924
+ }
22925
+ async function readFinalArtifact(sandboxDir, outputPath) {
22926
+ const artifactPath = join42(sandboxDir, outputPath);
22927
+ try {
22928
+ const content = await readFile22(artifactPath, "utf-8");
22929
+ return { artifact: JSON.parse(content) };
22930
+ } catch (error2) {
22931
+ const errorMessage = `Failed to read final artifact from ${outputPath}: ${error2 instanceof Error ? error2.message : String(error2)}`;
22932
+ const isParseError = error2 instanceof Error && error2.message.includes("JSON");
22933
+ if (isParseError) {
22934
+ return {
22935
+ error: {
22936
+ success: false,
22937
+ error: {
22938
+ type: "malformed_output",
22939
+ expected: "valid JSON",
22940
+ got: "invalid JSON",
22941
+ message: errorMessage
22942
+ }
22943
+ }
22944
+ };
22945
+ }
22946
+ return { error: validationError("artifact", errorMessage) };
22947
+ }
22948
+ }
22949
+ async function extractSandboxResult(options) {
22950
+ const loopliaHome = getLoopliaPluginPath();
22951
+ const sandboxRoot = options?.sandboxRoot ?? join42(loopliaHome, "sandbox");
22952
+ const sandboxId = options?.sandboxId;
22953
+ const sandboxDir = await resolveSandboxDir(sandboxRoot, sandboxId);
22954
+ if (!sandboxDir) {
22955
+ const message = sandboxId ? `Sandbox not found: ${sandboxId}` : "No sandbox directories found";
22956
+ return validationError("sandbox", message);
22957
+ }
22958
+ const { manifest, error: readError } = await readValidationManifest(sandboxDir);
22959
+ if (!manifest) {
22960
+ return validationError("validation.json", readError ?? "Unknown error");
22961
+ }
22962
+ const { allValidated, pendingSteps } = areAllStepsValidated(manifest.steps);
22963
+ if (!allValidated) {
22964
+ return validationError(
22965
+ "steps",
22966
+ `Workflow incomplete. Pending steps: ${pendingSteps.join(", ")}`
22967
+ );
22968
+ }
22969
+ const finalStep = resolveFinalStep(manifest.steps, manifest.finalStepId);
22970
+ if (!finalStep) {
22971
+ return validationError("steps", "No steps found in validation manifest");
22972
+ }
22973
+ const { artifact, error: artifactError } = await readFinalArtifact(
22974
+ sandboxDir,
22975
+ finalStep.output
22976
+ );
22977
+ if (artifactError) {
22978
+ return artifactError;
22979
+ }
22980
+ return {
22981
+ success: true,
22982
+ data: {
22983
+ status: "success",
22984
+ sandboxId: manifest.sandboxId ?? sandboxDir.split("/").pop(),
22985
+ workflowId: manifest.workflow,
22986
+ artifact
22987
+ }
22988
+ };
22989
+ }
22688
22990
  function expandPath(path) {
22689
22991
  if (path.startsWith("~/") || path === "~") {
22690
22992
  const home = homedir32();
22691
22993
  if (!home) {
22692
22994
  throw new Error("Unable to determine home directory");
22693
22995
  }
22694
- const expanded = path === "~" ? home : join42(home, path.slice(2));
22996
+ const expanded = path === "~" ? home : join52(home, path.slice(2));
22695
22997
  return normalize(expanded);
22696
22998
  }
22697
22999
  if (isAbsolute(path)) {
@@ -22700,26 +23002,26 @@ function expandPath(path) {
22700
23002
  return normalize(resolve(path));
22701
23003
  }
22702
23004
  function getPluginPath() {
22703
- return join42(process.cwd(), "plugins", "looplia-writer");
23005
+ return join52(process.cwd(), "plugins", "looplia-writer");
22704
23006
  }
22705
23007
  function getPluginPaths2() {
22706
- const base = join42(process.cwd(), "plugins");
23008
+ const base = join52(process.cwd(), "plugins");
22707
23009
  return {
22708
- core: join42(base, "looplia-core"),
22709
- writer: join42(base, "looplia-writer")
23010
+ core: join52(base, "looplia-core"),
23011
+ writer: join52(base, "looplia-writer")
22710
23012
  };
22711
23013
  }
22712
23014
  async function checkRequiredFiles(workspaceDir) {
22713
23015
  const requiredPaths = [
22714
23016
  // Core structure
22715
- join42(workspaceDir, "CLAUDE.md"),
22716
- join42(workspaceDir, ".claude", "agents"),
22717
- join42(workspaceDir, ".claude", "skills"),
22718
- join42(workspaceDir, "workflows"),
23017
+ join52(workspaceDir, "CLAUDE.md"),
23018
+ join52(workspaceDir, ".claude", "agents"),
23019
+ join52(workspaceDir, ".claude", "skills"),
23020
+ join52(workspaceDir, "workflows"),
22719
23021
  // From looplia-core plugin
22720
- join42(workspaceDir, ".claude", "commands"),
22721
- join42(workspaceDir, ".claude", "skills", "workflow-executor"),
22722
- join42(workspaceDir, ".claude", "skills", "workflow-validator")
23022
+ join52(workspaceDir, ".claude", "commands"),
23023
+ join52(workspaceDir, ".claude", "skills", "workflow-executor"),
23024
+ join52(workspaceDir, ".claude", "skills", "workflow-validator")
22723
23025
  ];
22724
23026
  for (const path of requiredPaths) {
22725
23027
  if (!await pathExists(path)) {
@@ -22748,33 +23050,33 @@ async function createTestWorkspace(workspaceDir, force) {
22748
23050
  await rm22(workspaceDir, { recursive: true, force: true });
22749
23051
  }
22750
23052
  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 });
23053
+ await mkdir22(join52(workspaceDir, ".claude", "agents"), { recursive: true });
23054
+ await mkdir22(join52(workspaceDir, ".claude", "skills"), { recursive: true });
23055
+ await mkdir22(join52(workspaceDir, "sandbox"), { recursive: true });
22754
23056
  const plugins = getPluginPaths2();
22755
- const writerWorkflowsDir = join42(plugins.writer, "workflows");
23057
+ const writerWorkflowsDir = join52(plugins.writer, "workflows");
22756
23058
  if (await pathExists(writerWorkflowsDir)) {
22757
- await cp2(writerWorkflowsDir, join42(workspaceDir, "workflows"), {
23059
+ await cp2(writerWorkflowsDir, join52(workspaceDir, "workflows"), {
22758
23060
  recursive: true
22759
23061
  });
22760
23062
  } else {
22761
- await mkdir22(join42(workspaceDir, "workflows"), { recursive: true });
23063
+ await mkdir22(join52(workspaceDir, "workflows"), { recursive: true });
22762
23064
  }
22763
- const coreValidatorDir = join42(plugins.core, "skills", "workflow-validator");
23065
+ const coreValidatorDir = join52(plugins.core, "skills", "workflow-validator");
22764
23066
  if (await pathExists(coreValidatorDir)) {
22765
23067
  await cp2(
22766
23068
  coreValidatorDir,
22767
- join42(workspaceDir, ".claude", "skills", "workflow-validator"),
23069
+ join52(workspaceDir, ".claude", "skills", "workflow-validator"),
22768
23070
  { recursive: true }
22769
23071
  );
22770
23072
  }
22771
23073
  await writeFile22(
22772
- join42(workspaceDir, "CLAUDE.md"),
23074
+ join52(workspaceDir, "CLAUDE.md"),
22773
23075
  "# Test Workspace\n",
22774
23076
  "utf-8"
22775
23077
  );
22776
23078
  await writeFile22(
22777
- join42(workspaceDir, "user-profile.json"),
23079
+ join52(workspaceDir, "user-profile.json"),
22778
23080
  JSON.stringify(createDefaultProfile2(), null, 2),
22779
23081
  "utf-8"
22780
23082
  );
@@ -22785,63 +23087,63 @@ async function bootstrapFromPlugins(workspaceDir, plugins) {
22785
23087
  await rm22(workspaceDir, { recursive: true, force: true });
22786
23088
  }
22787
23089
  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");
23090
+ await mkdir22(join52(workspaceDir, ".claude"), { recursive: true });
23091
+ await mkdir22(join52(workspaceDir, "sandbox"), { recursive: true });
23092
+ const coreCommandsDir = join52(plugins.core, "commands");
22791
23093
  if (await pathExists(coreCommandsDir)) {
22792
- await cp2(coreCommandsDir, join42(workspaceDir, ".claude", "commands"), {
23094
+ await cp2(coreCommandsDir, join52(workspaceDir, ".claude", "commands"), {
22793
23095
  recursive: true
22794
23096
  });
22795
23097
  }
22796
- const coreSkillsDir = join42(plugins.core, "skills");
23098
+ const coreSkillsDir = join52(plugins.core, "skills");
22797
23099
  if (await pathExists(coreSkillsDir)) {
22798
- await cp2(coreSkillsDir, join42(workspaceDir, ".claude", "skills"), {
23100
+ await cp2(coreSkillsDir, join52(workspaceDir, ".claude", "skills"), {
22799
23101
  recursive: true
22800
23102
  });
22801
23103
  }
22802
- const coreHooksDir = join42(plugins.core, "hooks");
23104
+ const coreHooksDir = join52(plugins.core, "hooks");
22803
23105
  if (await pathExists(coreHooksDir)) {
22804
- await cp2(coreHooksDir, join42(workspaceDir, ".claude", "hooks"), {
23106
+ await cp2(coreHooksDir, join52(workspaceDir, ".claude", "hooks"), {
22805
23107
  recursive: true
22806
23108
  });
22807
23109
  }
22808
- const coreAgentsDir = join42(plugins.core, "agents");
23110
+ const coreAgentsDir = join52(plugins.core, "agents");
22809
23111
  if (await pathExists(coreAgentsDir)) {
22810
- await mkdir22(join42(workspaceDir, ".claude", "agents"), { recursive: true });
22811
- await cp2(coreAgentsDir, join42(workspaceDir, ".claude", "agents"), {
23112
+ await mkdir22(join52(workspaceDir, ".claude", "agents"), { recursive: true });
23113
+ await cp2(coreAgentsDir, join52(workspaceDir, ".claude", "agents"), {
22812
23114
  recursive: true
22813
23115
  });
22814
23116
  }
22815
- const coreScriptsDir = join42(plugins.core, "scripts");
23117
+ const coreScriptsDir = join52(plugins.core, "scripts");
22816
23118
  if (await pathExists(coreScriptsDir)) {
22817
- await cp2(coreScriptsDir, join42(workspaceDir, "scripts"), {
23119
+ await cp2(coreScriptsDir, join52(workspaceDir, "scripts"), {
22818
23120
  recursive: true
22819
23121
  });
22820
23122
  }
22821
- const coreClaudeMd = join42(plugins.core, "CLAUDE.md");
23123
+ const coreClaudeMd = join52(plugins.core, "CLAUDE.md");
22822
23124
  if (await pathExists(coreClaudeMd)) {
22823
- await cp2(coreClaudeMd, join42(workspaceDir, "CLAUDE.md"));
23125
+ await cp2(coreClaudeMd, join52(workspaceDir, "CLAUDE.md"));
22824
23126
  }
22825
- const writerAgentsDir = join42(plugins.writer, "agents");
23127
+ const writerAgentsDir = join52(plugins.writer, "agents");
22826
23128
  if (await pathExists(writerAgentsDir)) {
22827
- await cp2(writerAgentsDir, join42(workspaceDir, ".claude", "agents"), {
23129
+ await cp2(writerAgentsDir, join52(workspaceDir, ".claude", "agents"), {
22828
23130
  recursive: true
22829
23131
  });
22830
23132
  }
22831
- const writerSkillsDir = join42(plugins.writer, "skills");
23133
+ const writerSkillsDir = join52(plugins.writer, "skills");
22832
23134
  if (await pathExists(writerSkillsDir)) {
22833
- await cp2(writerSkillsDir, join42(workspaceDir, ".claude", "skills"), {
23135
+ await cp2(writerSkillsDir, join52(workspaceDir, ".claude", "skills"), {
22834
23136
  recursive: true
22835
23137
  });
22836
23138
  }
22837
- const writerWorkflowsDir = join42(plugins.writer, "workflows");
23139
+ const writerWorkflowsDir = join52(plugins.writer, "workflows");
22838
23140
  if (await pathExists(writerWorkflowsDir)) {
22839
- await cp2(writerWorkflowsDir, join42(workspaceDir, "workflows"), {
23141
+ await cp2(writerWorkflowsDir, join52(workspaceDir, "workflows"), {
22840
23142
  recursive: true
22841
23143
  });
22842
23144
  }
22843
23145
  await writeFile22(
22844
- join42(workspaceDir, "user-profile.json"),
23146
+ join52(workspaceDir, "user-profile.json"),
22845
23147
  JSON.stringify(createDefaultProfile2(), null, 2),
22846
23148
  "utf-8"
22847
23149
  );
@@ -22879,12 +23181,12 @@ function getWorkspacePath(baseDir) {
22879
23181
  return expandPath(baseDir ?? "~/.looplia");
22880
23182
  }
22881
23183
  async function readUserProfile(workspaceDir) {
22882
- const profilePath = join42(workspaceDir, "user-profile.json");
22883
- const content = await readFile22(profilePath, "utf-8");
23184
+ const profilePath = join52(workspaceDir, "user-profile.json");
23185
+ const content = await readFile3(profilePath, "utf-8");
22884
23186
  return JSON.parse(content);
22885
23187
  }
22886
23188
  async function writeUserProfile(workspaceDir, profile) {
22887
- const profilePath = join42(workspaceDir, "user-profile.json");
23189
+ const profilePath = join52(workspaceDir, "user-profile.json");
22888
23190
  await writeFile22(profilePath, JSON.stringify(profile, null, 2), "utf-8");
22889
23191
  }
22890
23192
  var workspaceCache = /* @__PURE__ */ new Map();
@@ -22902,7 +23204,7 @@ async function getOrInitWorkspace(baseDir, useFilesystemExtensions) {
22902
23204
  return workspace;
22903
23205
  }
22904
23206
  function getApiKey(config2) {
22905
- return config2?.apiKey ?? process.env.ANTHROPIC_API_KEY ?? process.env.CLAUDE_CODE_OAUTH_TOKEN;
23207
+ return config2?.apiKey ?? process.env.ANTHROPIC_API_KEY ?? process.env.ANTHROPIC_AUTH_TOKEN ?? process.env.CLAUDE_CODE_OAUTH_TOKEN;
22906
23208
  }
22907
23209
  async function initializeAndValidateApiKey(config2) {
22908
23210
  const settings = await readLoopliaSettings();
@@ -22912,7 +23214,7 @@ async function initializeAndValidateApiKey(config2) {
22912
23214
  const apiKey = getApiKey(config2);
22913
23215
  if (!apiKey) {
22914
23216
  throw new Error(
22915
- "API key is required. Set ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN environment variable"
23217
+ "API key is required. Set ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, or CLAUDE_CODE_OAUTH_TOKEN environment variable"
22916
23218
  );
22917
23219
  }
22918
23220
  return apiKey;
@@ -23392,7 +23694,7 @@ function truncate(str, maxLen) {
23392
23694
  }
23393
23695
  return `${str.slice(0, maxLen - 3)}...`;
23394
23696
  }
23395
- async function* executeAgenticQueryStreaming(prompt, jsonSchema, config2) {
23697
+ async function* executeAgenticQueryStreaming(prompt, _jsonSchema, config2) {
23396
23698
  const resolvedConfig = resolveConfig(config2);
23397
23699
  await initializeAndValidateApiKey(config2);
23398
23700
  const mainModel = getMainModel();
@@ -23464,8 +23766,12 @@ When processing --file arguments or user file paths, resolve them against the Us
23464
23766
  "WebSearch",
23465
23767
  "WebFetch"
23466
23768
  ],
23467
- outputFormat: { type: "json_schema", schema: jsonSchema }
23769
+ // v0.7.2: Removed outputFormat to support non-Anthropic models
23770
+ // Final results are now extracted from sandbox output files via extractSandboxResult()
23468
23771
  // v0.6.9: No custom agents - using built-in general-purpose for workflow steps
23772
+ // v0.7.4: Pass runHooks from config if provided (workflow protection)
23773
+ // Only the run command passes hooks - other SDK usage doesn't need them
23774
+ ...config2?.runHooks && { hooks: config2.runHooks }
23469
23775
  }
23470
23776
  });
23471
23777
  let finalResult;
@@ -23489,6 +23795,12 @@ When processing --file arguments or user file paths, resolve them against the Us
23489
23795
  }
23490
23796
  logger.close();
23491
23797
  yield progressTracker.onComplete();
23798
+ if (!finalResult) {
23799
+ finalResult = await extractSandboxResult({
23800
+ sandboxId: process.env.LOOPLIA_SANDBOX_ID,
23801
+ sandboxRoot: process.env.LOOPLIA_SANDBOX_ROOT ?? join6(workspace, "sandbox")
23802
+ });
23803
+ }
23492
23804
  return finalResult ?? {
23493
23805
  success: false,
23494
23806
  error: { type: "unknown", message: "No result received" }
@@ -23633,6 +23945,337 @@ function createClaudeAgentExecutor(config2) {
23633
23945
  }
23634
23946
  };
23635
23947
  }
23948
+ var JSON_EXTENSION_REGEX = /\.json$/;
23949
+ var LOCK_TIMEOUT_MS = 3e4;
23950
+ var LOCK_RETRY_DELAY_MS2 = 200;
23951
+ async function findMostRecentSandbox2(sandboxRoot) {
23952
+ try {
23953
+ const entries = await readdir22(sandboxRoot, { withFileTypes: true });
23954
+ const dirs = entries.filter(
23955
+ (e) => e.isDirectory() && !e.name.startsWith(".")
23956
+ );
23957
+ if (dirs.length === 0) {
23958
+ return;
23959
+ }
23960
+ const dirStats = await Promise.all(
23961
+ dirs.map(async (dir) => {
23962
+ const dirPath = join7(sandboxRoot, dir.name);
23963
+ const dirStat = await stat2(dirPath);
23964
+ return { path: dirPath, mtime: dirStat.mtime.getTime() };
23965
+ })
23966
+ );
23967
+ dirStats.sort((a, b) => b.mtime - a.mtime);
23968
+ return dirStats[0]?.path;
23969
+ } catch {
23970
+ return;
23971
+ }
23972
+ }
23973
+ async function readValidationManifest2(sandboxDir) {
23974
+ const validationPath = join7(sandboxDir, "validation.json");
23975
+ try {
23976
+ const content = await readFile4(validationPath, "utf-8");
23977
+ return JSON.parse(content);
23978
+ } catch {
23979
+ return;
23980
+ }
23981
+ }
23982
+ function resolveSandboxRoot(context) {
23983
+ return context?.sandboxRoot ?? join7(getLoopliaPluginPath(), "sandbox");
23984
+ }
23985
+ async function resolveSandboxDir2(context) {
23986
+ const sandboxRoot = resolveSandboxRoot(context);
23987
+ if (context?.sandboxId) {
23988
+ return join7(sandboxRoot, context.sandboxId);
23989
+ }
23990
+ return await findMostRecentSandbox2(sandboxRoot);
23991
+ }
23992
+ function isRecord(value) {
23993
+ return typeof value === "object" && value !== null;
23994
+ }
23995
+ function getSandboxArtifactInfo(filePath) {
23996
+ if (!filePath) {
23997
+ return;
23998
+ }
23999
+ if (!(filePath.includes("/sandbox/") && filePath.includes("/outputs/"))) {
24000
+ return;
24001
+ }
24002
+ const outputsIndex = filePath.lastIndexOf("/outputs/");
24003
+ if (outputsIndex < 0) {
24004
+ return;
24005
+ }
24006
+ const sandboxDir = filePath.slice(0, outputsIndex);
24007
+ const filename = filePath.slice(outputsIndex + "/outputs/".length);
24008
+ const artifact = filename.replace(JSON_EXTENSION_REGEX, "");
24009
+ return { sandboxDir, artifact };
24010
+ }
24011
+ async function readArtifactData(filePath, artifact) {
24012
+ try {
24013
+ const content = await readFile4(filePath, "utf-8");
24014
+ return JSON.parse(content);
24015
+ } catch {
24016
+ console.error(`Validation failed for ${artifact}: Invalid JSON`);
24017
+ return;
24018
+ }
24019
+ }
24020
+ function validateWorkflowArtifact(data, criteria) {
24021
+ const checks = [];
24022
+ if (criteria.required_fields) {
24023
+ checks.push(...checkRequiredFields(data, criteria.required_fields));
24024
+ }
24025
+ if (criteria.min_quotes !== void 0) {
24026
+ checks.push(checkMinQuotes(data, criteria.min_quotes));
24027
+ }
24028
+ if (criteria.min_key_points !== void 0) {
24029
+ checks.push(checkMinKeyPoints(data, criteria.min_key_points));
24030
+ }
24031
+ if (criteria.min_outline_sections !== void 0) {
24032
+ checks.push(checkMinOutlineSections(data, criteria.min_outline_sections));
24033
+ }
24034
+ if (criteria.has_hooks === true) {
24035
+ checks.push(checkHasHooks(data));
24036
+ }
24037
+ const passed = checks.every((check2) => check2.passed);
24038
+ return { passed, checks };
24039
+ }
24040
+ function checkRequiredFields(data, fields) {
24041
+ return fields.map((field) => {
24042
+ const exists = hasField(data, field);
24043
+ return {
24044
+ name: `has_${field}`,
24045
+ passed: exists,
24046
+ message: exists ? "OK" : `Missing required field: ${field}`
24047
+ };
24048
+ });
24049
+ }
24050
+ function checkMinQuotes(data, minQuotes) {
24051
+ const quotes = getArrayLength(data, "importantQuotes");
24052
+ const passed = quotes >= minQuotes;
24053
+ return {
24054
+ name: "min_quotes",
24055
+ passed,
24056
+ message: passed ? `Found ${quotes} quotes (min: ${minQuotes})` : `Found ${quotes} quotes, need at least ${minQuotes}`
24057
+ };
24058
+ }
24059
+ function checkMinKeyPoints(data, minPoints) {
24060
+ const bullets = getArrayLength(data, "bullets");
24061
+ const keyPoints = getArrayLength(data, "keyPoints");
24062
+ const count = Math.max(bullets, keyPoints);
24063
+ const passed = count >= minPoints;
24064
+ return {
24065
+ name: "min_key_points",
24066
+ passed,
24067
+ message: passed ? `Found ${count} key points (min: ${minPoints})` : `Found ${count} key points, need at least ${minPoints}`
24068
+ };
24069
+ }
24070
+ function checkMinOutlineSections(data, minSections) {
24071
+ const sections = getArrayLength(data, "suggestedOutline");
24072
+ const passed = sections >= minSections;
24073
+ return {
24074
+ name: "min_outline_sections",
24075
+ passed,
24076
+ message: passed ? `Found ${sections} outline sections (min: ${minSections})` : `Found ${sections} sections, need at least ${minSections}`
24077
+ };
24078
+ }
24079
+ function checkHasHooks(data) {
24080
+ const hooksCount = getHooksCount(data);
24081
+ const passed = hooksCount > 0;
24082
+ return {
24083
+ name: "has_hooks",
24084
+ passed,
24085
+ message: passed ? `Found ${hooksCount} hooks` : "No hooks found in artifact"
24086
+ };
24087
+ }
24088
+ function getHooksCount(data) {
24089
+ const ideas = data.ideas;
24090
+ if (ideas && Array.isArray(ideas.hooks)) {
24091
+ return ideas.hooks.length;
24092
+ }
24093
+ if (Array.isArray(data.hooks)) {
24094
+ return data.hooks.length;
24095
+ }
24096
+ return 0;
24097
+ }
24098
+ function hasField(data, field) {
24099
+ const parts = field.split(".");
24100
+ let current = data;
24101
+ for (const part of parts) {
24102
+ if (current === null || typeof current !== "object") {
24103
+ return false;
24104
+ }
24105
+ current = current[part];
24106
+ }
24107
+ return current !== void 0 && current !== null;
24108
+ }
24109
+ function getArrayLength(data, field) {
24110
+ const value = data[field];
24111
+ return Array.isArray(value) ? value.length : 0;
24112
+ }
24113
+ function validateArtifactData(artifact, artifactData, criteria) {
24114
+ if (criteria === void 0 || criteria === null) {
24115
+ return true;
24116
+ }
24117
+ if (!isRecord(criteria)) {
24118
+ console.error(`Validation failed for ${artifact}: Invalid criteria`);
24119
+ return false;
24120
+ }
24121
+ const result = validateWorkflowArtifact(
24122
+ artifactData,
24123
+ criteria
24124
+ );
24125
+ if (!result.passed) {
24126
+ console.error(`Semantic validation failed for ${artifact}:`);
24127
+ console.error(JSON.stringify(result, null, 2));
24128
+ }
24129
+ return result.passed;
24130
+ }
24131
+ async function sleep2(ms) {
24132
+ await new Promise((resolve2) => setTimeout(resolve2, ms));
24133
+ }
24134
+ async function acquireLock(lockPath, timeoutMs) {
24135
+ const start = Date.now();
24136
+ while (Date.now() - start < timeoutMs) {
24137
+ try {
24138
+ const handle = await open2(lockPath, "wx");
24139
+ return {
24140
+ release: async () => {
24141
+ await handle.close();
24142
+ try {
24143
+ await unlink(lockPath);
24144
+ } catch {
24145
+ }
24146
+ }
24147
+ };
24148
+ } catch (error2) {
24149
+ if (error2 instanceof Error && "code" in error2 && error2.code === "EEXIST") {
24150
+ await sleep2(LOCK_RETRY_DELAY_MS2);
24151
+ continue;
24152
+ }
24153
+ throw error2;
24154
+ }
24155
+ }
24156
+ return;
24157
+ }
24158
+ async function writeValidationManifest(validationPath, manifest) {
24159
+ const tempPath = `${validationPath}.tmp`;
24160
+ await writeFile3(tempPath, JSON.stringify(manifest, null, 2), "utf-8");
24161
+ await rename2(tempPath, validationPath);
24162
+ }
24163
+ async function findMissingOutputs(sandboxDir, steps) {
24164
+ const missing = [];
24165
+ for (const [stepId, stepState] of Object.entries(steps)) {
24166
+ const outputPath = stepState.output;
24167
+ if (!outputPath) {
24168
+ continue;
24169
+ }
24170
+ const fullPath = join7(sandboxDir, outputPath);
24171
+ try {
24172
+ await stat2(fullPath);
24173
+ } catch {
24174
+ missing.push(stepId);
24175
+ }
24176
+ }
24177
+ return missing;
24178
+ }
24179
+ function findPendingSteps(steps) {
24180
+ return Object.entries(steps).filter(([, stepState]) => !stepState.validated).map(([stepId]) => stepId);
24181
+ }
24182
+ function createStopGuardHook(context) {
24183
+ return async (input) => {
24184
+ const hookInput = input;
24185
+ if (hookInput.stop_hook_active === true) {
24186
+ return {};
24187
+ }
24188
+ const sandboxDir = await resolveSandboxDir2(context);
24189
+ if (!sandboxDir) {
24190
+ return {};
24191
+ }
24192
+ const manifest = await readValidationManifest2(sandboxDir);
24193
+ if (!manifest) {
24194
+ return {};
24195
+ }
24196
+ const missingOutputs = await findMissingOutputs(sandboxDir, manifest.steps);
24197
+ if (missingOutputs.length > 0) {
24198
+ const stepsList = missingOutputs.join(", ");
24199
+ return {
24200
+ decision: "block",
24201
+ 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.`
24202
+ };
24203
+ }
24204
+ const pendingSteps = findPendingSteps(manifest.steps);
24205
+ if (pendingSteps.length > 0) {
24206
+ const stepsList = pendingSteps.join(", ");
24207
+ return {
24208
+ decision: "block",
24209
+ 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.`
24210
+ };
24211
+ }
24212
+ return {};
24213
+ };
24214
+ }
24215
+ var stopGuardHook = createStopGuardHook();
24216
+ function createPostWriteValidateHook() {
24217
+ return async (input) => {
24218
+ const hookInput = input;
24219
+ const filePath = hookInput.tool_input?.file_path;
24220
+ const artifactInfo = getSandboxArtifactInfo(filePath);
24221
+ if (!(artifactInfo && filePath)) {
24222
+ return {};
24223
+ }
24224
+ const artifactData = await readArtifactData(
24225
+ filePath,
24226
+ artifactInfo.artifact
24227
+ );
24228
+ if (!artifactData) {
24229
+ return {};
24230
+ }
24231
+ const validationPath = join7(artifactInfo.sandboxDir, "validation.json");
24232
+ const lock = await acquireLock(`${validationPath}.lock`, LOCK_TIMEOUT_MS);
24233
+ if (!lock) {
24234
+ console.error("Failed to acquire lock on validation.json");
24235
+ return {};
24236
+ }
24237
+ try {
24238
+ const manifest = await readValidationManifest2(artifactInfo.sandboxDir);
24239
+ if (!manifest) {
24240
+ return {};
24241
+ }
24242
+ const stepState = manifest.steps[artifactInfo.artifact];
24243
+ if (!stepState) {
24244
+ return {};
24245
+ }
24246
+ const isValid3 = validateArtifactData(
24247
+ artifactInfo.artifact,
24248
+ artifactData,
24249
+ stepState.validate
24250
+ );
24251
+ if (!isValid3) {
24252
+ return {};
24253
+ }
24254
+ manifest.steps[artifactInfo.artifact] = {
24255
+ ...stepState,
24256
+ validated: true,
24257
+ validatedAt: (/* @__PURE__ */ new Date()).toISOString()
24258
+ };
24259
+ await writeValidationManifest(validationPath, manifest);
24260
+ console.error(`\u2713 Validated: ${artifactInfo.artifact}.json`);
24261
+ } finally {
24262
+ await lock.release();
24263
+ }
24264
+ return {};
24265
+ };
24266
+ }
24267
+ var postWriteValidateHook = createPostWriteValidateHook();
24268
+ function createWorkflowHooks() {
24269
+ const context = {
24270
+ sandboxId: process.env.LOOPLIA_SANDBOX_ID,
24271
+ sandboxRoot: process.env.LOOPLIA_SANDBOX_ROOT
24272
+ };
24273
+ return {
24274
+ Stop: [{ hooks: [createStopGuardHook(context)] }],
24275
+ SubagentStop: [{ hooks: [createStopGuardHook(context)] }],
24276
+ PostToolUse: [{ matcher: "Write", hooks: [createPostWriteValidateHook()] }]
24277
+ };
24278
+ }
23636
24279
  var SUMMARIZE_SYSTEM_PROMPT = `You are an expert content analyst specializing in summarization and content intelligence.
23637
24280
 
23638
24281
  Your expertise includes:
@@ -23663,7 +24306,7 @@ The content-analyzer agent will:
23663
24306
  3. Use content-documenter skill for structured documentation
23664
24307
  4. Write results to sandbox/${content.id}/outputs/summary.json`;
23665
24308
  }
23666
- async function* executeInteractiveQueryStreaming(prompt, jsonSchema, config2, questionCallback) {
24309
+ async function* executeInteractiveQueryStreaming(prompt, _jsonSchema, config2, questionCallback) {
23667
24310
  const resolvedConfig = resolveConfig(config2);
23668
24311
  await initializeAndValidateApiKey(config2);
23669
24312
  const mainModel = getMainModel();
@@ -23761,8 +24404,9 @@ When processing --file arguments or user file paths, resolve them against the Us
23761
24404
  "WebFetch",
23762
24405
  "AskUserQuestion"
23763
24406
  // Only in interactive mode
23764
- ],
23765
- outputFormat: { type: "json_schema", schema: jsonSchema }
24407
+ ]
24408
+ // v0.7.2: Removed outputFormat to support non-Anthropic models
24409
+ // Final results are now extracted from sandbox output files via extractSandboxResult()
23766
24410
  }
23767
24411
  });
23768
24412
  let finalResult;
@@ -23786,6 +24430,12 @@ When processing --file arguments or user file paths, resolve them against the Us
23786
24430
  }
23787
24431
  logger.close();
23788
24432
  yield progressTracker.onComplete();
24433
+ if (!finalResult) {
24434
+ finalResult = await extractSandboxResult({
24435
+ sandboxId: process.env.LOOPLIA_SANDBOX_ID,
24436
+ sandboxRoot: process.env.LOOPLIA_SANDBOX_ROOT ?? join8(workspace, "sandbox")
24437
+ });
24438
+ }
23789
24439
  return finalResult ?? {
23790
24440
  success: false,
23791
24441
  error: { type: "unknown", message: "No result received" }
@@ -24147,6 +24797,7 @@ export {
24147
24797
  writeUserProfile,
24148
24798
  executeAgenticQueryStreaming,
24149
24799
  createClaudeAgentExecutor,
24800
+ createWorkflowHooks,
24150
24801
  SUMMARIZE_SYSTEM_PROMPT,
24151
24802
  buildSummarizePrompt,
24152
24803
  executeInteractiveQueryStreaming,