@chanlerdev/scorel 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2762,12 +2762,7 @@ var init_tools = __esm({
2762
2762
  return {
2763
2763
  content: [{
2764
2764
  type: "text",
2765
- text: [
2766
- `Snipped user turn ${result.anchorUserEventId}.`,
2767
- `Hidden through ${result.throughEventId}.`,
2768
- `${result.hiddenEventCount} event(s) will be omitted from future model context.`,
2769
- "Original session JSONL remains unchanged."
2770
- ].join(" ")
2765
+ text: "Snipped the selected user turn. It will be omitted from future model context."
2771
2766
  }],
2772
2767
  details: result
2773
2768
  };
@@ -3419,15 +3414,65 @@ var init_memory = __esm({
3419
3414
  }
3420
3415
  });
3421
3416
 
3417
+ // packages/core/src/reminders/index.ts
3418
+ var createSystemReminderBlock, renderSystemReminderText, renderSystemReminder, systemReminderMessage, appendSystemReminderToToolResult, cloneSystemReminderBlock, isToolResultWithContent;
3419
+ var init_reminders = __esm({
3420
+ "packages/core/src/reminders/index.ts"() {
3421
+ "use strict";
3422
+ createSystemReminderBlock = (input) => ({
3423
+ type: "system_reminder",
3424
+ kind: input.kind,
3425
+ origin: input.origin,
3426
+ text: input.text,
3427
+ visibility: input.visibility,
3428
+ scope: input.scope,
3429
+ ...input.data ? { data: { ...input.data } } : {}
3430
+ });
3431
+ renderSystemReminderText = (text) => `<system-reminder>
3432
+ ${text}
3433
+ </system-reminder>`;
3434
+ renderSystemReminder = (input) => renderSystemReminderText(typeof input === "string" ? input : input.text);
3435
+ systemReminderMessage = (block, meta) => ({
3436
+ role: "user",
3437
+ content: [cloneSystemReminderBlock(block)],
3438
+ ...meta ? { meta: { ...meta } } : {}
3439
+ });
3440
+ appendSystemReminderToToolResult = (message, block) => {
3441
+ for (let i = message.content.length - 1; i >= 0; i -= 1) {
3442
+ const candidate = message.content[i];
3443
+ if (candidate?.type !== "tool_result" || !isToolResultWithContent(candidate.result)) {
3444
+ continue;
3445
+ }
3446
+ const mergedResult = {
3447
+ ...candidate.result,
3448
+ content: [...candidate.result.content, cloneSystemReminderBlock(block)]
3449
+ };
3450
+ message.content[i] = {
3451
+ ...candidate,
3452
+ result: mergedResult
3453
+ };
3454
+ return true;
3455
+ }
3456
+ return false;
3457
+ };
3458
+ cloneSystemReminderBlock = (block) => ({
3459
+ ...block,
3460
+ ...block.data ? { data: { ...block.data } } : {}
3461
+ });
3462
+ isToolResultWithContent = (value) => typeof value === "object" && value !== null && "content" in value && Array.isArray(value.content);
3463
+ }
3464
+ });
3465
+
3422
3466
  // packages/core/src/provider/pi-ai.ts
3423
3467
  import {
3424
3468
  getModels,
3425
3469
  streamSimple
3426
3470
  } from "@mariozechner/pi-ai";
3427
- var DEFAULT_CUSTOM_MODEL_CONTEXT_WINDOW, DEFAULT_CUSTOM_MODEL_MAX_TOKENS, createPiAiProvider, resolvePiAiModel, toPiContext, toPiMessage, toPiAssistantBlock, fromPiAssistant, fromPiContentBlock, toPiTool, textContent, toolResultText, stringMeta, toPiStopReason, fromPiStopReason, fromPiUsage;
3471
+ var DEFAULT_CUSTOM_MODEL_CONTEXT_WINDOW, DEFAULT_CUSTOM_MODEL_MAX_TOKENS, createPiAiProvider, resolvePiAiModel, toPiContext, toPiMessage, toPiAssistantBlock, fromPiAssistant, fromPiContentBlock, toPiTool, textContent, toolResultText, isSystemReminderContentBlock, stringMeta, toPiStopReason, fromPiStopReason, fromPiUsage;
3428
3472
  var init_pi_ai = __esm({
3429
3473
  "packages/core/src/provider/pi-ai.ts"() {
3430
3474
  "use strict";
3475
+ init_reminders();
3431
3476
  DEFAULT_CUSTOM_MODEL_CONTEXT_WINDOW = 2e5;
3432
3477
  DEFAULT_CUSTOM_MODEL_MAX_TOKENS = 64e3;
3433
3478
  createPiAiProvider = (options) => ({
@@ -3526,6 +3571,9 @@ var init_pi_ai = __esm({
3526
3571
  if (block.type === "text") {
3527
3572
  return [{ type: "text", text: block.text }];
3528
3573
  }
3574
+ if (block.type === "system_reminder") {
3575
+ return [{ type: "text", text: renderSystemReminder(block) }];
3576
+ }
3529
3577
  if (block.type === "thinking") {
3530
3578
  return [{ type: "thinking", thinking: block.text }];
3531
3579
  }
@@ -3564,16 +3612,33 @@ var init_pi_ai = __esm({
3564
3612
  description: tool.description,
3565
3613
  parameters: tool.parameters
3566
3614
  });
3567
- textContent = (message) => message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
3615
+ textContent = (message) => message.content.flatMap((block) => {
3616
+ if (block.type === "text") {
3617
+ return [block.text];
3618
+ }
3619
+ if (block.type === "system_reminder") {
3620
+ return [renderSystemReminder(block)];
3621
+ }
3622
+ return [];
3623
+ }).join("\n");
3568
3624
  toolResultText = (result) => {
3569
3625
  if (typeof result === "object" && result !== null && "content" in result) {
3570
3626
  const content = result.content;
3571
3627
  if (Array.isArray(content)) {
3572
- return content.filter((block) => block?.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n");
3628
+ return content.flatMap((block) => {
3629
+ if (block?.type === "text" && typeof block.text === "string") {
3630
+ return [block.text];
3631
+ }
3632
+ if (isSystemReminderContentBlock(block)) {
3633
+ return [renderSystemReminder(block)];
3634
+ }
3635
+ return [];
3636
+ }).join("\n");
3573
3637
  }
3574
3638
  }
3575
3639
  return JSON.stringify(result);
3576
3640
  };
3641
+ isSystemReminderContentBlock = (value) => typeof value === "object" && value !== null && value.type === "system_reminder" && typeof value.text === "string";
3577
3642
  stringMeta = (message, key) => {
3578
3643
  const value = message.meta?.[key];
3579
3644
  return typeof value === "string" ? value : void 0;
@@ -3855,11 +3920,12 @@ function assertTreeEvent(value) {
3855
3920
  throw new SessionStoreError("invalid_event", "skill_index_delta is missing delta payload");
3856
3921
  }
3857
3922
  }
3858
- var snipUserMessageAlias, SessionStoreError, SessionTree, JsonlSession, sessionFilePath, sessionLogFilePath, sessionArtifactsDirPath, createSession, loadSession, buildContext, hiddenContextEventIds, retainedMessagesBeforeCompact, isRetainedContextStart, parseJsonLine, parseHeader, parseSessionEvent, validateSessionMatch, isConversationEvent, isInstructionSnapshot, isHarnessItem, isCompactEvent, isContextControlEvent, isQueueUpdate, isSessionTitleUpdated, isSkillIndexSnapshot, isSkillIndexDelta, isSkillIndexEntry, appendHarnessItemToContext, appendReminderToToolResult, isToolResultWithContent, renderSystemReminder, compactSummaryMessage, cloneMessage, isRecord8;
3923
+ var snipUserMessageAlias, SessionStoreError, SessionTree, JsonlSession, sessionFilePath, sessionLogFilePath, sessionArtifactsDirPath, createSession, loadSession, buildContext, hiddenContextEventIds, retainedMessagesBeforeCompact, isRetainedContextStart, parseJsonLine, parseHeader, parseSessionEvent, validateSessionMatch, isConversationEvent, isInstructionSnapshot, isHarnessItem, isCompactEvent, isContextControlEvent, isQueueUpdate, isSessionTitleUpdated, isSkillIndexSnapshot, isSkillIndexDelta, isSkillIndexEntry, appendHarnessItemToContext, compactSummaryMessage, reminderKindFromHarness, reminderVisibilityFromHarness, reminderScopeFromHarness, cloneMessage, isRecord8;
3859
3924
  var init_session = __esm({
3860
3925
  "packages/core/src/session/index.ts"() {
3861
3926
  "use strict";
3862
3927
  init_src();
3928
+ init_reminders();
3863
3929
  snipUserMessageAlias = (eventId) => `u_${createHash2("sha256").update(eventId).digest("hex").slice(0, 8)}`;
3864
3930
  SessionStoreError = class extends Error {
3865
3931
  code;
@@ -4214,65 +4280,74 @@ var init_session = __esm({
4214
4280
  );
4215
4281
  isSkillIndexEntry = (value) => isRecord8(value) && typeof value.name === "string" && typeof value.path === "string" && (value.scope === "user" || value.scope === "project" || value.scope === "extension") && typeof value.description === "string" && typeof value.mtimeMs === "number" && typeof value.size === "number" && typeof value.contentHash === "string" && typeof value.priority === "number";
4216
4282
  appendHarnessItemToContext = (messages, event) => {
4217
- const reminder = renderSystemReminder(event.item.content);
4283
+ const reminder = createSystemReminderBlock({
4284
+ kind: reminderKindFromHarness(event.item.kind),
4285
+ origin: event.item.origin,
4286
+ text: event.item.content,
4287
+ visibility: reminderVisibilityFromHarness(event.item.visibility),
4288
+ scope: reminderScopeFromHarness(event.item.kind),
4289
+ ...event.item.data ? { data: event.item.data } : {}
4290
+ });
4218
4291
  const last = messages.at(-1);
4219
- if (last?.role === "tool_result" && appendReminderToToolResult(last, reminder)) {
4292
+ if (last?.role === "tool_result" && appendSystemReminderToToolResult(last, reminder)) {
4220
4293
  return;
4221
4294
  }
4222
- messages.push({
4223
- role: "user",
4224
- content: [{ type: "text", text: reminder }],
4225
- meta: {
4226
- source: "harness_item",
4227
- harnessKind: event.item.kind,
4228
- harnessOrigin: event.item.origin
4229
- }
4230
- });
4231
- };
4232
- appendReminderToToolResult = (message, reminder) => {
4233
- for (let i = message.content.length - 1; i >= 0; i -= 1) {
4234
- const block = message.content[i];
4235
- if (block?.type !== "tool_result" || !isToolResultWithContent(block.result)) {
4236
- continue;
4237
- }
4238
- const mergedResult = {
4239
- ...block.result,
4240
- content: [...block.result.content, { type: "text", text: `
4241
-
4242
- ${reminder}` }]
4243
- };
4244
- message.content[i] = {
4245
- ...block,
4246
- result: mergedResult
4247
- };
4248
- return true;
4249
- }
4250
- return false;
4295
+ messages.push(systemReminderMessage(reminder, {
4296
+ source: "harness_item",
4297
+ harnessKind: event.item.kind,
4298
+ harnessOrigin: event.item.origin
4299
+ }));
4251
4300
  };
4252
- isToolResultWithContent = (value) => isRecord8(value) && Array.isArray(value.content);
4253
- renderSystemReminder = (content) => `<system-reminder>
4254
- ${content}
4255
- </system-reminder>`;
4256
4301
  compactSummaryMessage = (event) => ({
4257
4302
  role: "user",
4258
- content: [{
4259
- type: "text",
4260
- text: renderSystemReminder([
4303
+ content: [createSystemReminderBlock({
4304
+ kind: "compact_summary",
4305
+ origin: "system",
4306
+ text: [
4261
4307
  "Earlier session context has been compacted.",
4262
4308
  "",
4263
4309
  event.summary.trim(),
4264
4310
  "",
4265
4311
  "Use this summary as continuity context. Verify current repository facts before acting."
4266
- ].join("\n"))
4267
- }],
4312
+ ].join("\n"),
4313
+ visibility: "model",
4314
+ scope: "session"
4315
+ })],
4268
4316
  meta: {
4269
4317
  source: "compact",
4270
4318
  compactedThrough: event.compactedThrough
4271
4319
  }
4272
4320
  });
4321
+ reminderKindFromHarness = (kind) => {
4322
+ if (kind === "attachment" || kind === "skill_listing" || kind === "skill_delta" || kind === "memory" || kind === "channel_context" || kind === "steer" || kind === "runtime_notice") {
4323
+ return kind;
4324
+ }
4325
+ if (kind === "date_change") {
4326
+ return "time";
4327
+ }
4328
+ return "runtime_notice";
4329
+ };
4330
+ reminderVisibilityFromHarness = (visibility) => {
4331
+ if (visibility === "hidden") {
4332
+ return "model";
4333
+ }
4334
+ return visibility;
4335
+ };
4336
+ reminderScopeFromHarness = (kind) => {
4337
+ if (kind === "steer" || kind === "skill_delta" || kind === "runtime_notice") {
4338
+ return "next_model_call";
4339
+ }
4340
+ if (kind === "channel_context" || kind === "attachment" || kind === "date_change") {
4341
+ return "turn";
4342
+ }
4343
+ return "session";
4344
+ };
4273
4345
  cloneMessage = (message) => ({
4274
4346
  ...message,
4275
4347
  content: message.content.map((block) => {
4348
+ if (block.type === "system_reminder") {
4349
+ return cloneSystemReminderBlock(block);
4350
+ }
4276
4351
  if (block.type !== "tool_result" || !isRecord8(block.result)) {
4277
4352
  return { ...block };
4278
4353
  }
@@ -4532,6 +4607,7 @@ var init_src3 = __esm({
4532
4607
  init_instructions();
4533
4608
  init_memory();
4534
4609
  init_pi_ai();
4610
+ init_reminders();
4535
4611
  init_runtime();
4536
4612
  init_session();
4537
4613
  init_skills();
@@ -5490,20 +5566,22 @@ var init_src4 = __esm({
5490
5566
  }
5491
5567
  async #handleLoadSession(connection, request) {
5492
5568
  try {
5493
- const lane = await this.#getLane(request.sessionId);
5569
+ const lane = this.#sessions.get(request.sessionId);
5570
+ const session = lane?.session ?? await loadSession({ sessionsDir: this.#sessionsDir, sessionId: request.sessionId });
5494
5571
  await this.#appendDiagnostic(request.sessionId, "session_loaded", { clientId: connection.clientId });
5495
5572
  connection.sessionId = request.sessionId;
5496
- const persistentEvents = [...lane.session.tree];
5573
+ const persistentEvents = [...session.tree];
5497
5574
  const sessionEvents = this.#events.get(request.sessionId) ?? [];
5498
5575
  if (sessionEvents.length === 0 && persistentEvents.length > 0) {
5499
5576
  this.#events.set(request.sessionId, persistentEvents);
5500
5577
  }
5578
+ this.#seqs.set(request.sessionId, Number(session.currentSeq));
5501
5579
  this.#respond(connection, request, {
5502
5580
  sessionId: request.sessionId,
5503
- activeLeafId: lane.session.activeLeafId,
5504
- currentSeq: lane.session.currentSeq,
5581
+ activeLeafId: session.activeLeafId,
5582
+ currentSeq: session.currentSeq,
5505
5583
  events: persistentEvents,
5506
- meta: lane.session.header.meta
5584
+ meta: session.header.meta
5507
5585
  });
5508
5586
  } catch (cause) {
5509
5587
  connection.emit({
@@ -7711,11 +7789,14 @@ var init_src4 = __esm({
7711
7789
  countContentBlocks = (message, type) => message.content.filter((block) => block.type === type).length;
7712
7790
  normalizeContent = (content) => typeof content === "string" ? [{ type: "text", text: content }] : content;
7713
7791
  snipUserMessageIdBlock = (userEventId) => ({
7714
- type: "text",
7715
- text: `<system-reminder>
7716
- snip.userMessageId: ${snipUserMessageAlias(userEventId)}
7717
- </system-reminder>`,
7718
- visibility: "model"
7792
+ ...createSystemReminderBlock({
7793
+ kind: "message_ref",
7794
+ origin: "system",
7795
+ text: `snip.userMessageId: ${snipUserMessageAlias(userEventId)}`,
7796
+ visibility: "model",
7797
+ scope: "message",
7798
+ data: { userMessageId: snipUserMessageAlias(userEventId) }
7799
+ })
7719
7800
  });
7720
7801
  inputText = (message) => message.content.flatMap((block) => block.type === "text" && block.visibility !== "model" ? [block.text] : []).join("\n").trim();
7721
7802
  assistantText = (message) => message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n").trim();
@@ -7733,6 +7814,9 @@ snip.userMessageId: ${snipUserMessageAlias(userEventId)}
7733
7814
  if (block.type === "tool_result") {
7734
7815
  return `[tool_result:${block.toolName}] ${JSON.stringify(block.result)}`;
7735
7816
  }
7817
+ if (block.type === "system_reminder") {
7818
+ return `[system_reminder:${block.kind}] ${block.text}`;
7819
+ }
7736
7820
  return "";
7737
7821
  }).filter(Boolean).join("\n").trim();
7738
7822
  return text || "(empty)";
@@ -9732,7 +9816,7 @@ import { createInterface } from "node:readline/promises";
9732
9816
  import { homedir as homedir9 } from "node:os";
9733
9817
  import { fileURLToPath as fileURLToPath5 } from "node:url";
9734
9818
  import { basename as basename4, dirname as dirname13, join as join17 } from "node:path";
9735
- var cliAppName, cliClientDependency, cliDaemonDependency, defaultSessionsDir, defaultStateDir4, runCli, runProject, runLogs, runAttach, attachCacheScope, attachCacheFilePath, attachDiagnosticsFilePath, findAttachDiagnosticsFilePath, stateDirFromSessionsDir, AttachDiagnostics, readAttachCache, writeAttachCache, emptyAttachCacheSnapshot, mergePersistentEvents, highestSeq, highestCachedStreamSeq, updateAttachCacheSnapshot, removeCompletedTransients, isCachedTransientMessage, AsyncInputQueue, parseAttachOptions, parseLogsOptions, runChat, createSigintHandler, loadOrCreateSession, parseChatOptions, requireValue6, promptIfInteractive, writeUsage, writeProjectUsage, writeEventError, writeToolResult, redactDiagnosticFields, formatDiagnosticLine2, formatDiagnosticValue2, AttachEventRenderer, blocksToText, isCliEntrypoint;
9819
+ var cliAppName, cliClientDependency, cliDaemonDependency, defaultSessionsDir, defaultStateDir4, runCli, runProject, runLogs, runAttach, attachCacheScope, attachCacheFilePath, attachDiagnosticsFilePath, findAttachDiagnosticsFilePath, stateDirFromSessionsDir, AttachDiagnostics, readAttachCache, writeAttachCache, emptyAttachCacheSnapshot, mergePersistentEvents, highestSeq, highestCachedStreamSeq, updateAttachCacheSnapshot, removeCompletedTransients, isCachedTransientMessage, AsyncInputQueue, parseAttachOptions, parseLogsOptions, runChat, runHeadless, renderRunEvent, renderRunFinal, writeJsonLine, RunTimeoutError, withRunTimeout, makeRunSummary, writeRunSummary, readRunPrompt, resolveRunConfig, stripTrailingSlashes2, createSigintHandler, loadOrCreateSession, parseChatOptions, parseRunOptions, parseRunOutputFormat, parsePositiveInteger, parseModelSelection, parseRunProviderApi, requireValue6, promptIfInteractive, writeUsage, writeRunUsage, writeProjectUsage, writeEventError, writeToolResult, redactDiagnosticFields, formatDiagnosticLine2, formatDiagnosticValue2, AttachEventRenderer, blocksToText, isCliEntrypoint;
9736
9820
  var init_index = __esm({
9737
9821
  async "apps/cli/src/index.ts"() {
9738
9822
  "use strict";
@@ -9766,6 +9850,19 @@ var init_index = __esm({
9766
9850
  const sessionsDir = runOptions.sessionsDir ?? chatOptions.sessionsDir;
9767
9851
  return runChat({ ...chatOptions, config: runOptions.config, sessionsDir, stateDir: stateDirFromSessionsDir(sessionsDir) }, io);
9768
9852
  }
9853
+ if (command === "run") {
9854
+ if (rest.includes("--help") || rest.includes("-h")) {
9855
+ writeRunUsage(io.output);
9856
+ return 0;
9857
+ }
9858
+ try {
9859
+ return runHeadless(parseRunOptions(rest, runOptions), io);
9860
+ } catch (cause) {
9861
+ io.error.write(`scorel run error: ${cause instanceof Error ? cause.message : String(cause)}
9862
+ `);
9863
+ return 2;
9864
+ }
9865
+ }
9769
9866
  if (command === "daemon") {
9770
9867
  return runCliDaemon(rest, {
9771
9868
  stateDir: stateDirFromSessionsDir(runOptions.sessionsDir),
@@ -10327,7 +10424,7 @@ var init_index = __esm({
10327
10424
  process.on("SIGINT", sigintHandler);
10328
10425
  try {
10329
10426
  await client.connect(options.sessionId);
10330
- const resumed = await loadOrCreateSession(client, options, project.projectId);
10427
+ const resumed = await loadOrCreateSession(client, options.sessionId, project.projectId);
10331
10428
  io.error.write(`scorel chat ${resumed ? "resumed" : "created"} session ${options.sessionId}
10332
10429
  `);
10333
10430
  const rl = createInterface({ input: io.input, crlfDelay: Infinity });
@@ -10375,6 +10472,247 @@ var init_index = __esm({
10375
10472
  await daemon.shutdown();
10376
10473
  }
10377
10474
  };
10475
+ runHeadless = async (options, io) => {
10476
+ const startedAt = Date.now();
10477
+ let projectId;
10478
+ const textParts = [];
10479
+ const prompt = await readRunPrompt(options, io);
10480
+ const runConfig = resolveRunConfig(options);
10481
+ const configScope = { scorelHomeDir: options.stateDir };
10482
+ const loadProjectConfig = async (project) => runConfig ?? options.config ?? await loadScorelConfig({ cwd: project.workDir, ...configScope });
10483
+ const loadProjectConfigProfile = async (project) => runConfig ?? options.config ?? await loadScorelConfigProfile({ cwd: project.workDir, ...configScope });
10484
+ const daemon = new ScorelHost({
10485
+ sessionsDir: options.sessionsDir,
10486
+ projectsPath: join17(options.stateDir, "projects.json"),
10487
+ deviceId: asDeviceId("device_local"),
10488
+ scorelHomeDir: options.stateDir,
10489
+ loadConfig: async ({ project }) => loadProjectConfig(project),
10490
+ loadConfigProfile: async ({ project }) => loadProjectConfigProfile(project),
10491
+ createRuntime: async ({ sessionId, project, selectedModel, purpose }) => createRealRuntime({
10492
+ cwd: project.workDir,
10493
+ config: await loadProjectConfig(project),
10494
+ sessionsDir: options.sessionsDir,
10495
+ sessionId,
10496
+ modelSelection: selectedModel ? { modelId: selectedModel.modelId, role: selectedModel.role } : void 0,
10497
+ includeTools: purpose === "chat"
10498
+ })
10499
+ });
10500
+ const client = new DaemonClient(createEmbeddedTransport(daemon), {
10501
+ clientId: asClientId("client_cli_run")
10502
+ });
10503
+ let unsubscribe;
10504
+ try {
10505
+ await daemon.start();
10506
+ const project = await daemon.registerProject(options.cwd);
10507
+ projectId = project.projectId;
10508
+ await client.connect(options.sessionId);
10509
+ await loadOrCreateSession(client, options.sessionId, project.projectId, options.modelSelection);
10510
+ unsubscribe = client.subscribe((event) => {
10511
+ if (event.type === "text_delta") {
10512
+ textParts.push(event.delta);
10513
+ }
10514
+ renderRunEvent(options, io, event);
10515
+ });
10516
+ const send = client.sendMessage(prompt, options.modelSelection ? { modelSelection: options.modelSelection } : void 0);
10517
+ const result = options.timeoutMs === void 0 ? await send : await withRunTimeout(send, options.timeoutMs, async () => {
10518
+ await client.cancel().catch(() => void 0);
10519
+ });
10520
+ const summary = makeRunSummary({
10521
+ options,
10522
+ startedAt,
10523
+ projectId,
10524
+ status: "completed",
10525
+ exitReason: "completed",
10526
+ userEventId: String(result.userEventId),
10527
+ assistantEventId: String(result.assistantEventId)
10528
+ });
10529
+ await writeRunSummary(options.summaryPath, summary);
10530
+ renderRunFinal(options, io, summary, textParts.join(""));
10531
+ return 0;
10532
+ } catch (cause) {
10533
+ const isTimeout = cause instanceof RunTimeoutError;
10534
+ const summary = makeRunSummary({
10535
+ options,
10536
+ startedAt,
10537
+ projectId,
10538
+ status: isTimeout ? "timeout" : "error",
10539
+ exitReason: isTimeout ? "timeout" : "error",
10540
+ error: cause instanceof Error ? cause : new Error(String(cause))
10541
+ });
10542
+ await writeRunSummary(options.summaryPath, summary).catch(() => void 0);
10543
+ renderRunFinal(options, io, summary, textParts.join(""));
10544
+ if (!options.quiet && options.outputFormat === "text") {
10545
+ io.error.write(`scorel run error: ${summary.error?.message ?? "unknown error"}
10546
+ `);
10547
+ }
10548
+ return isTimeout ? 124 : 1;
10549
+ } finally {
10550
+ unsubscribe?.();
10551
+ client.disconnect();
10552
+ await daemon.shutdown();
10553
+ }
10554
+ };
10555
+ renderRunEvent = (options, io, event) => {
10556
+ if (options.outputFormat === "none" || options.outputFormat === "json") {
10557
+ return;
10558
+ }
10559
+ if (options.outputFormat === "stream-json") {
10560
+ writeJsonLine(io.output, { type: "event", event });
10561
+ return;
10562
+ }
10563
+ if (event.type === "text_delta") {
10564
+ io.output.write(event.delta);
10565
+ }
10566
+ if (event.type === "tool_result") {
10567
+ writeToolResult(io.output, event);
10568
+ }
10569
+ if (event.type === "error") {
10570
+ writeEventError(io.error, event);
10571
+ }
10572
+ };
10573
+ renderRunFinal = (options, io, summary, text) => {
10574
+ if (options.outputFormat === "none") {
10575
+ return;
10576
+ }
10577
+ if (options.outputFormat === "json") {
10578
+ io.output.write(`${JSON.stringify({ ...summary, result: text })}
10579
+ `);
10580
+ return;
10581
+ }
10582
+ if (options.outputFormat === "stream-json") {
10583
+ writeJsonLine(io.output, { type: "result", summary, result: text });
10584
+ return;
10585
+ }
10586
+ if (!text.endsWith("\n")) {
10587
+ io.output.write("\n");
10588
+ }
10589
+ };
10590
+ writeJsonLine = (output, value) => {
10591
+ output.write(`${JSON.stringify(value)}
10592
+ `);
10593
+ };
10594
+ RunTimeoutError = class extends Error {
10595
+ constructor(timeoutMs) {
10596
+ super(`run timed out after ${timeoutMs}ms`);
10597
+ this.name = "RunTimeoutError";
10598
+ }
10599
+ };
10600
+ withRunTimeout = async (promise, timeoutMs, onTimeout) => {
10601
+ let timeout;
10602
+ try {
10603
+ return await Promise.race([
10604
+ promise,
10605
+ new Promise((_resolve, reject) => {
10606
+ timeout = setTimeout(() => {
10607
+ void onTimeout().finally(() => reject(new RunTimeoutError(timeoutMs)));
10608
+ }, timeoutMs);
10609
+ })
10610
+ ]);
10611
+ } finally {
10612
+ if (timeout) {
10613
+ clearTimeout(timeout);
10614
+ }
10615
+ }
10616
+ };
10617
+ makeRunSummary = (input) => ({
10618
+ status: input.status,
10619
+ sessionId: String(input.options.sessionId),
10620
+ ...input.projectId ? { projectId: String(input.projectId) } : {},
10621
+ cwd: input.options.cwd,
10622
+ stateDir: input.options.stateDir,
10623
+ sessionsDir: input.options.sessionsDir,
10624
+ sessionJsonl: join17(input.options.sessionsDir, `${input.options.sessionId}.jsonl`),
10625
+ outputFormat: input.options.outputFormat,
10626
+ elapsedMs: Date.now() - input.startedAt,
10627
+ exitReason: input.exitReason,
10628
+ ...input.userEventId ? { userEventId: input.userEventId } : {},
10629
+ ...input.assistantEventId ? { assistantEventId: input.assistantEventId } : {},
10630
+ ...input.error ? { error: { message: input.error.message } } : {}
10631
+ });
10632
+ writeRunSummary = async (path, summary) => {
10633
+ if (!path) {
10634
+ return;
10635
+ }
10636
+ await mkdir8(dirname13(path), { recursive: true });
10637
+ await writeFile8(path, `${JSON.stringify(summary, null, 2)}
10638
+ `);
10639
+ };
10640
+ readRunPrompt = async (options, io) => {
10641
+ if (options.promptSource === "prompt-file") {
10642
+ return (await readFile14(options.promptFile, "utf8")).trim();
10643
+ }
10644
+ if (options.promptSource === "stdin") {
10645
+ const chunks = [];
10646
+ for await (const chunk of io.input) {
10647
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
10648
+ }
10649
+ return Buffer.concat(chunks).toString("utf8").trim();
10650
+ }
10651
+ return (options.prompt ?? "").trim();
10652
+ };
10653
+ resolveRunConfig = (options) => {
10654
+ const override = options.providerOverride;
10655
+ if (!override || !override.provider && !override.api && !override.baseUrl && !override.apiKey) {
10656
+ return void 0;
10657
+ }
10658
+ if (!override.baseUrl) {
10659
+ throw new Error("--base-url is required when overriding the run provider");
10660
+ }
10661
+ if (!override.apiKey) {
10662
+ throw new Error("--api-key is required when overriding the run provider");
10663
+ }
10664
+ if (!options.modelSelection?.modelId) {
10665
+ throw new Error("--model <model-id> is required when overriding the run provider");
10666
+ }
10667
+ const providerId = "run";
10668
+ const providerModelId = "run_model";
10669
+ const availableModelId = options.modelSelection.modelId;
10670
+ return {
10671
+ providers: {
10672
+ [providerId]: {
10673
+ type: "custom",
10674
+ api: override.api ?? "openai-completions",
10675
+ provider: override.provider ?? "openai",
10676
+ baseUrl: stripTrailingSlashes2(override.baseUrl),
10677
+ apiKey: override.apiKey
10678
+ }
10679
+ },
10680
+ providerModels: {
10681
+ [providerModelId]: {
10682
+ provider: providerId,
10683
+ id: availableModelId,
10684
+ displayName: availableModelId
10685
+ }
10686
+ },
10687
+ models: {
10688
+ [availableModelId]: {
10689
+ model: providerModelId,
10690
+ displayName: availableModelId
10691
+ }
10692
+ },
10693
+ modelProfile: {
10694
+ roles: {
10695
+ primary: availableModelId,
10696
+ standard: availableModelId,
10697
+ auxiliary: availableModelId
10698
+ }
10699
+ },
10700
+ memory: {
10701
+ enabled: false,
10702
+ daily: false,
10703
+ sessionMemory: false,
10704
+ autoDream: false,
10705
+ promoteRoot: false,
10706
+ dreamIdleMinutes: 60,
10707
+ autoCompactThreshold: 0.8
10708
+ },
10709
+ runtime: {
10710
+ tokenSavingRtk: false
10711
+ },
10712
+ extensions: {}
10713
+ };
10714
+ };
10715
+ stripTrailingSlashes2 = (value) => value.replace(/\/+$/, "");
10378
10716
  createSigintHandler = (options) => {
10379
10717
  return () => {
10380
10718
  if (options.isInFlight()) {
@@ -10385,14 +10723,14 @@ var init_index = __esm({
10385
10723
  options.exit();
10386
10724
  };
10387
10725
  };
10388
- loadOrCreateSession = async (client, options, projectId) => {
10726
+ loadOrCreateSession = async (client, sessionId, projectId, modelSelection) => {
10389
10727
  try {
10390
- await client.loadSession(options.sessionId);
10728
+ await client.loadSession(sessionId);
10391
10729
  return true;
10392
10730
  } catch {
10393
10731
  await client.createSession({
10394
- sessionId: options.sessionId,
10395
- meta: { projectId }
10732
+ sessionId,
10733
+ meta: { projectId, ...modelSelection ? { modelSelection } : {} }
10396
10734
  });
10397
10735
  return false;
10398
10736
  }
@@ -10417,6 +10755,152 @@ var init_index = __esm({
10417
10755
  const sessionsDir = defaultSessionsDir();
10418
10756
  return { sessionId, sessionsDir, stateDir: stateDirFromSessionsDir(sessionsDir), cwd };
10419
10757
  };
10758
+ parseRunOptions = (argv, runOptions) => {
10759
+ const promptSources = [];
10760
+ let sessionId = asSessionId(`ses_run_${Date.now().toString(36)}`);
10761
+ let cwd = process.cwd();
10762
+ let stateDir;
10763
+ let sessionsDir = runOptions.sessionsDir;
10764
+ let timeoutMs;
10765
+ let outputFormat = "text";
10766
+ let summaryPath;
10767
+ let quiet = false;
10768
+ let modelSelection;
10769
+ const providerOverride = {};
10770
+ const positional = [];
10771
+ for (let index = 0; index < argv.length; index += 1) {
10772
+ const arg = argv[index];
10773
+ if (arg === "--prompt") {
10774
+ promptSources.push({ promptSource: "prompt", prompt: requireValue6(argv, index, "--prompt") });
10775
+ index += 1;
10776
+ continue;
10777
+ }
10778
+ if (arg === "--prompt-file") {
10779
+ promptSources.push({ promptSource: "prompt-file", promptFile: requireValue6(argv, index, "--prompt-file") });
10780
+ index += 1;
10781
+ continue;
10782
+ }
10783
+ if (arg === "--stdin") {
10784
+ promptSources.push({ promptSource: "stdin" });
10785
+ continue;
10786
+ }
10787
+ if (arg === "--session") {
10788
+ sessionId = asSessionId(requireValue6(argv, index, "--session"));
10789
+ index += 1;
10790
+ continue;
10791
+ }
10792
+ if (arg === "--cwd") {
10793
+ cwd = requireValue6(argv, index, "--cwd");
10794
+ index += 1;
10795
+ continue;
10796
+ }
10797
+ if (arg === "--state-dir") {
10798
+ stateDir = requireValue6(argv, index, "--state-dir");
10799
+ index += 1;
10800
+ continue;
10801
+ }
10802
+ if (arg === "--sessions-dir") {
10803
+ sessionsDir = requireValue6(argv, index, "--sessions-dir");
10804
+ index += 1;
10805
+ continue;
10806
+ }
10807
+ if (arg === "--timeout-ms") {
10808
+ timeoutMs = parsePositiveInteger(requireValue6(argv, index, "--timeout-ms"), "--timeout-ms");
10809
+ index += 1;
10810
+ continue;
10811
+ }
10812
+ if (arg === "--output-format") {
10813
+ outputFormat = parseRunOutputFormat(requireValue6(argv, index, "--output-format"));
10814
+ index += 1;
10815
+ continue;
10816
+ }
10817
+ if (arg === "--summary") {
10818
+ summaryPath = requireValue6(argv, index, "--summary");
10819
+ index += 1;
10820
+ continue;
10821
+ }
10822
+ if (arg === "--quiet") {
10823
+ quiet = true;
10824
+ continue;
10825
+ }
10826
+ if (arg === "--model") {
10827
+ modelSelection = parseModelSelection(requireValue6(argv, index, "--model"));
10828
+ index += 1;
10829
+ continue;
10830
+ }
10831
+ if (arg === "--provider") {
10832
+ providerOverride.provider = requireValue6(argv, index, "--provider");
10833
+ index += 1;
10834
+ continue;
10835
+ }
10836
+ if (arg === "--api" || arg === "--protocol") {
10837
+ providerOverride.api = parseRunProviderApi(requireValue6(argv, index, arg));
10838
+ index += 1;
10839
+ continue;
10840
+ }
10841
+ if (arg === "--base-url" || arg === "--baseurl") {
10842
+ providerOverride.baseUrl = requireValue6(argv, index, arg);
10843
+ index += 1;
10844
+ continue;
10845
+ }
10846
+ if (arg === "--api-key" || arg === "--apikey") {
10847
+ providerOverride.apiKey = requireValue6(argv, index, arg);
10848
+ index += 1;
10849
+ continue;
10850
+ }
10851
+ if (arg.startsWith("-")) {
10852
+ throw new Error(`Unknown run option: ${arg}`);
10853
+ }
10854
+ positional.push(arg);
10855
+ }
10856
+ if (positional.length > 0) {
10857
+ promptSources.unshift({ promptSource: "argument", prompt: positional.join(" ") });
10858
+ }
10859
+ if (promptSources.length !== 1) {
10860
+ throw new Error("scorel run requires exactly one prompt source");
10861
+ }
10862
+ const resolvedStateDir = stateDir ?? stateDirFromSessionsDir(sessionsDir);
10863
+ const resolvedSessionsDir = sessionsDir ?? join17(resolvedStateDir, "sessions");
10864
+ return {
10865
+ ...promptSources[0],
10866
+ sessionId,
10867
+ cwd,
10868
+ stateDir: resolvedStateDir,
10869
+ sessionsDir: resolvedSessionsDir,
10870
+ timeoutMs,
10871
+ outputFormat,
10872
+ summaryPath,
10873
+ quiet,
10874
+ modelSelection,
10875
+ providerOverride,
10876
+ config: runOptions.config
10877
+ };
10878
+ };
10879
+ parseRunOutputFormat = (value) => {
10880
+ if (value === "text" || value === "json" || value === "stream-json" || value === "none") {
10881
+ return value;
10882
+ }
10883
+ throw new Error("--output-format must be text, json, stream-json, or none");
10884
+ };
10885
+ parsePositiveInteger = (value, flag) => {
10886
+ const parsed = Number(value);
10887
+ if (!Number.isInteger(parsed) || parsed <= 0) {
10888
+ throw new Error(`${flag} must be a positive integer`);
10889
+ }
10890
+ return parsed;
10891
+ };
10892
+ parseModelSelection = (value) => {
10893
+ if (value === "primary" || value === "standard" || value === "auxiliary") {
10894
+ return { role: value };
10895
+ }
10896
+ return { modelId: value };
10897
+ };
10898
+ parseRunProviderApi = (value) => {
10899
+ if (value === "openai-completions" || value === "openai-responses" || value === "google-generative-ai" || value === "anthropic-messages") {
10900
+ return value;
10901
+ }
10902
+ throw new Error("--api must be openai-completions, openai-responses, google-generative-ai, or anthropic-messages");
10903
+ };
10420
10904
  requireValue6 = (argv, index, flag) => {
10421
10905
  const value = argv[index + 1];
10422
10906
  if (!value) {
@@ -10434,6 +10918,12 @@ var init_index = __esm({
10434
10918
  [
10435
10919
  "Usage: scorel chat [--session <id>] [--cwd <dir>]",
10436
10920
  " scorel [--session <id>] [--cwd <dir>]",
10921
+ " scorel run [prompt] [--prompt <text> | --prompt-file <path> | --stdin]",
10922
+ " [--cwd <dir>] [--state-dir <dir>] [--sessions-dir <dir>]",
10923
+ " [--session <id>] [--timeout-ms <ms>]",
10924
+ " [--output-format text|json|stream-json|none] [--summary <path>]",
10925
+ " [--provider <name>] [--api|--protocol <protocol>]",
10926
+ " [--base-url|--baseurl <url>] [--api-key|--apikey <key>] [--model <id>]",
10437
10927
  " scorel attach --session <id> --remote <ws-url> --token <token>",
10438
10928
  " scorel host start [--host <h>] [--port <p>] [--token <t>] [--project <dir>]",
10439
10929
  " [--relay <relay-url> | --no-relay] [--replace] [--idle-timeout-ms <ms>]",
@@ -10456,6 +10946,37 @@ var init_index = __esm({
10456
10946
  ].join("\n") + "\n"
10457
10947
  );
10458
10948
  };
10949
+ writeRunUsage = (output) => {
10950
+ output.write(
10951
+ [
10952
+ "Usage: scorel run [prompt]",
10953
+ " scorel run --prompt <text>",
10954
+ " scorel run --prompt-file <path>",
10955
+ " scorel run --stdin",
10956
+ "",
10957
+ "Options:",
10958
+ " --cwd <dir>",
10959
+ " --state-dir <dir>",
10960
+ " --sessions-dir <dir>",
10961
+ " --session <id>",
10962
+ " --timeout-ms <ms>",
10963
+ " --output-format text|json|stream-json|none",
10964
+ " --summary <path>",
10965
+ " --quiet",
10966
+ " --model <primary|standard|auxiliary|model-id>",
10967
+ " --provider <name>",
10968
+ " --api, --protocol <openai-completions|openai-responses|google-generative-ai|anthropic-messages>",
10969
+ " --base-url, --baseurl <url>",
10970
+ " --api-key, --apikey <key>",
10971
+ "",
10972
+ "Examples:",
10973
+ ' scorel run --prompt "Summarize this project" --output-format json',
10974
+ " scorel run --prompt-file /tmp/instruction.txt --cwd /workspace --state-dir /tmp/scorel-state \\",
10975
+ ' --api openai-completions --baseurl http://127.0.0.1:4000/v1 --apikey "$API_KEY" \\',
10976
+ " --model gpt-5.4-mini --output-format none --summary /logs/agent/scorel-summary.json"
10977
+ ].join("\n") + "\n"
10978
+ );
10979
+ };
10459
10980
  writeProjectUsage = (output) => {
10460
10981
  output.write("Usage: scorel project list | add <dir> | remove <project-id>\n");
10461
10982
  };
@@ -10575,7 +11096,18 @@ ${text}
10575
11096
  this.#atLineStart = text.endsWith("\n");
10576
11097
  }
10577
11098
  };
10578
- blocksToText = (blocks) => blocks.filter((block) => block.type === "text").map((block) => block.text).join("");
11099
+ blocksToText = (blocks) => blocks.flatMap((block) => {
11100
+ if (block.type === "text") {
11101
+ if (block.visibility === "model") {
11102
+ return [];
11103
+ }
11104
+ return [block.text];
11105
+ }
11106
+ if (block.type === "system_reminder" && block.visibility !== "model") {
11107
+ return [block.text];
11108
+ }
11109
+ return [];
11110
+ }).join("");
10579
11111
  isCliEntrypoint = async () => {
10580
11112
  if (!process.argv[1]) return false;
10581
11113
  const [argvPath, modulePath] = await Promise.all([