@pqpo/pragma 0.1.0-next.14 → 0.1.0-next.15

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.
Files changed (2) hide show
  1. package/dist/cli.js +158 -20
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -35515,6 +35515,25 @@ var init_output = __esm({
35515
35515
  }
35516
35516
  });
35517
35517
 
35518
+ // packages/core/dist/execution/human-interaction-checkpoint.js
35519
+ function isHumanInteractionCheckpointError(error53) {
35520
+ return error53 instanceof HumanInteractionCheckpointError;
35521
+ }
35522
+ var HumanInteractionCheckpointError;
35523
+ var init_human_interaction_checkpoint = __esm({
35524
+ "packages/core/dist/execution/human-interaction-checkpoint.js"() {
35525
+ "use strict";
35526
+ HumanInteractionCheckpointError = class extends Error {
35527
+ executionId;
35528
+ constructor(executionId) {
35529
+ super(`Execution checkpointed while waiting for human input: ${executionId}`);
35530
+ this.executionId = executionId;
35531
+ this.name = "HumanInteractionCheckpointError";
35532
+ }
35533
+ };
35534
+ }
35535
+ });
35536
+
35518
35537
  // packages/core/dist/execution/expert-orchestrator.js
35519
35538
  import { createHash as createHash6, randomUUID as randomUUID11 } from "node:crypto";
35520
35539
  function collectDescendantInvocationIds(invocations, ownerInvocationId) {
@@ -35679,6 +35698,7 @@ var init_expert_orchestrator = __esm({
35679
35698
  init_invocation_service();
35680
35699
  init_context_resolution_service();
35681
35700
  init_context_id_resolver();
35701
+ init_human_interaction_checkpoint();
35682
35702
  MAX_EXPERT_MESSAGE_CHARACTERS = 1e5;
35683
35703
  MAX_PENDING_EXPERT_MESSAGES = 128;
35684
35704
  MAX_PENDING_EXPERT_MESSAGE_BYTES = 1024 * 1024;
@@ -36401,6 +36421,7 @@ var init_expert_orchestrator = __esm({
36401
36421
  }
36402
36422
  }
36403
36423
  async executeActiveJob(options2) {
36424
+ let checkpointed = false;
36404
36425
  try {
36405
36426
  await this.options.store.commit({
36406
36427
  commitId: `agent-activated:${options2.invocation.invocationId}:${randomUUID11()}`,
@@ -36427,15 +36448,19 @@ var init_expert_orchestrator = __esm({
36427
36448
  prompt: options2.prompt,
36428
36449
  permit: options2.permit
36429
36450
  });
36430
- } catch {
36431
- const current = await this.options.store.getInvocation(this.options.executionId, options2.invocation.invocationId);
36432
- if (current !== void 0 && !isTerminalExecutionStatus(current.status)) {
36433
- await this.failUnrecoverableJob(current, "Expert task failed before reaching a terminal state.");
36451
+ } catch (error53) {
36452
+ if (isHumanInteractionCheckpointError(error53)) {
36453
+ checkpointed = true;
36454
+ } else {
36455
+ const current = await this.options.store.getInvocation(this.options.executionId, options2.invocation.invocationId);
36456
+ if (current !== void 0 && !isTerminalExecutionStatus(current.status)) {
36457
+ await this.failUnrecoverableJob(current, "Expert task failed before reaching a terminal state.");
36458
+ }
36434
36459
  }
36435
36460
  } finally {
36436
36461
  try {
36437
36462
  const currentAgent = await this.options.store.getAgent(this.options.executionId, options2.agentId);
36438
- if (currentAgent?.activeInvocationId === options2.invocation.invocationId) {
36463
+ if (!checkpointed && currentAgent?.activeInvocationId === options2.invocation.invocationId) {
36439
36464
  await this.options.store.commit({
36440
36465
  commitId: `agent-idle:${options2.invocation.invocationId}`,
36441
36466
  executionId: this.options.executionId,
@@ -37218,9 +37243,6 @@ var init_expert_prompt = __esm({
37218
37243
 
37219
37244
  // packages/core/dist/execution/expert-runner.js
37220
37245
  import { createHash as createHash7, randomUUID as randomUUID12 } from "node:crypto";
37221
- function isHumanInteractionCheckpointError(error53) {
37222
- return error53 instanceof HumanInteractionCheckpointError;
37223
- }
37224
37246
  async function listPendingHumanInteractionIds(store, executionId) {
37225
37247
  return (await readPendingHumanInteractions(store, executionId)).map((interaction) => interaction.interactionId);
37226
37248
  }
@@ -38325,7 +38347,7 @@ async function validateDelegatedRuntimeRouting(options2, delegation, parentRunti
38325
38347
  function sameRuntimeBinding2(left, right) {
38326
38348
  return left.runtimeId === right.runtimeId && left.revision === right.revision && left.fingerprint === right.fingerprint;
38327
38349
  }
38328
- var AUTOMATIC_EXPERT_WAIT_TIMEOUT_MS, HumanInteractionCheckpointError, ExecutionController;
38350
+ var AUTOMATIC_EXPERT_WAIT_TIMEOUT_MS, ExecutionController;
38329
38351
  var init_expert_runner = __esm({
38330
38352
  "packages/core/dist/execution/expert-runner.js"() {
38331
38353
  "use strict";
@@ -38355,15 +38377,9 @@ var init_expert_runner = __esm({
38355
38377
  init_runtime_session_pool();
38356
38378
  init_context_output_service();
38357
38379
  init_expert_prompt();
38380
+ init_human_interaction_checkpoint();
38381
+ init_human_interaction_checkpoint();
38358
38382
  AUTOMATIC_EXPERT_WAIT_TIMEOUT_MS = 10 * 60 * 1e3;
38359
- HumanInteractionCheckpointError = class extends Error {
38360
- executionId;
38361
- constructor(executionId) {
38362
- super(`Execution checkpointed while waiting for human input: ${executionId}`);
38363
- this.executionId = executionId;
38364
- this.name = "HumanInteractionCheckpointError";
38365
- }
38366
- };
38367
38383
  ExecutionController = class {
38368
38384
  executionId;
38369
38385
  store;
@@ -38459,7 +38475,7 @@ var init_expert_runner = __esm({
38459
38475
  signal.abort(checkpoint);
38460
38476
  const submission = this.activeRuntimeSubmissions.get(invocationId);
38461
38477
  if (submission !== void 0) {
38462
- runtimeStops.push(Promise.resolve().then(async () => await submission.handle.cancel()));
38478
+ runtimeStops.push((async () => await submission.handle.cancel())());
38463
38479
  }
38464
38480
  }
38465
38481
  for (const interaction of pending)
@@ -39245,6 +39261,57 @@ var init_expert_session = __esm({
39245
39261
  throw error53;
39246
39262
  }
39247
39263
  }
39264
+ async recoverClosedSession(expert, request2) {
39265
+ const existing = this.active.get(request2.sessionId);
39266
+ if (existing !== void 0)
39267
+ return existing;
39268
+ if (request2.reason.trim() === "") {
39269
+ throw new Error("Closed ExpertSession recovery requires a reason.");
39270
+ }
39271
+ const record2 = await this.dependencies.sessions.get(request2.sessionId);
39272
+ if (record2 === void 0)
39273
+ throw new Error(`ExpertSession not found: ${request2.sessionId}`);
39274
+ if (record2.status !== "closed") {
39275
+ throw new Error(`ExpertSession is not closed: ${request2.sessionId}`);
39276
+ }
39277
+ const currentFingerprint = fingerprintExpertExecutionDefinition(expert);
39278
+ const definitionMatches = record2.expertId === expert.id && record2.definitionFingerprint === currentFingerprint;
39279
+ const rootExpert = isExpertTeam(expert) ? expert.coordinator : expert;
39280
+ const migration = definitionMatches ? void 0 : validateDefinitionMigration(record2, expert, currentFingerprint, request2);
39281
+ if (!definitionMatches && migration === void 0) {
39282
+ throw new ExpertDefinitionMismatchError(request2.sessionId);
39283
+ }
39284
+ const claimId = randomUUID13();
39285
+ const recovered = await this.dependencies.sessions.recoverClosed({
39286
+ sessionId: request2.sessionId,
39287
+ expectedUpdatedAt: record2.updatedAt,
39288
+ claimId,
39289
+ leaseMs: EXPERT_SESSION_LEASE_MS,
39290
+ reason: request2.reason
39291
+ });
39292
+ if (recovered === void 0) {
39293
+ throw new Error(`ExpertSession recovery conflict: ${request2.sessionId}`);
39294
+ }
39295
+ try {
39296
+ if (migration !== void 0) {
39297
+ await this.migrateSessionDefinition({
39298
+ sessionId: request2.sessionId,
39299
+ expertId: expert.id,
39300
+ rootExpertId: rootExpert.id,
39301
+ definitionFingerprint: currentFingerprint,
39302
+ previousExpertId: migration.previousExpertId,
39303
+ previousRootExpertId: migration.previousRootExpertId,
39304
+ reason: migration.reason
39305
+ });
39306
+ }
39307
+ const session = this.createActiveSession(expert, request2.sessionId, true, claimId);
39308
+ this.active.set(request2.sessionId, session);
39309
+ return session;
39310
+ } catch (error53) {
39311
+ await this.dependencies.sessions.releaseLease(request2.sessionId, claimId);
39312
+ throw error53;
39313
+ }
39314
+ }
39248
39315
  async migrateSessionDefinition(options2) {
39249
39316
  return await this.dependencies.sessions.transact(options2.sessionId, async ({ session, prompts }) => {
39250
39317
  const rootContext = session.contexts[session.rootContextId];
@@ -41277,6 +41344,76 @@ function createFileExpertSessionStore(options2) {
41277
41344
  return parsed.data;
41278
41345
  });
41279
41346
  },
41347
+ async recoverClosed(input2) {
41348
+ return await withFileLock(paths.expertSessionLock(input2.sessionId), async () => {
41349
+ await prepareExpertSession(paths, options2.executions, input2.sessionId);
41350
+ const session = ExpertSessionRecordSchema.parse(await requireJson(paths.expertSessionState(input2.sessionId), input2.sessionId));
41351
+ if (session.status !== "closed" || session.updatedAt !== input2.expectedUpdatedAt) {
41352
+ return void 0;
41353
+ }
41354
+ if (session.activeExecutionId !== void 0) {
41355
+ throw new Error(`Closed ExpertSession has an active Execution: ${input2.sessionId}`);
41356
+ }
41357
+ const prompts = PromptRequestSchema.array().parse(await readJson(paths.expertSessionPrompts(input2.sessionId)) ?? []);
41358
+ if (prompts.some((prompt) => prompt.status === "queued" || prompt.status === "running")) {
41359
+ throw new Error(`Closed ExpertSession has pending prompts: ${input2.sessionId}`);
41360
+ }
41361
+ for (const executionId of session.executionIds) {
41362
+ const execution = await options2.executions.get(executionId);
41363
+ if (execution !== void 0 && !isTerminalExecutionStatus(execution.status)) {
41364
+ throw new Error(`Closed ExpertSession has a non-terminal Execution: ${input2.sessionId}`);
41365
+ }
41366
+ }
41367
+ const existingValue = await readJson(paths.expertSessionLease(input2.sessionId));
41368
+ const existing = existingValue === void 0 ? void 0 : ExpertSessionLeaseSchema.parse(existingValue);
41369
+ if (existing !== void 0 && existing.claimId !== input2.claimId && Date.parse(existing.expiresAt) > Date.now() && (existing.processId === void 0 || isProcessAlive2(existing.processId))) {
41370
+ return void 0;
41371
+ }
41372
+ const rootContext = session.contexts[session.rootContextId];
41373
+ if (rootContext === void 0)
41374
+ throw new Error("ExpertSession root Context is missing.");
41375
+ const reopenedRoot = {
41376
+ ...rootContext,
41377
+ lifecycle: "open",
41378
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
41379
+ };
41380
+ delete reopenedRoot.closedAt;
41381
+ const recovered = ExpertSessionRecordSchema.parse({
41382
+ ...session,
41383
+ status: "open",
41384
+ contexts: { ...session.contexts, [session.rootContextId]: reopenedRoot },
41385
+ updatedAt: reopenedRoot.updatedAt
41386
+ });
41387
+ const events = ExpertSessionEventSchema.array().parse(await readJson(paths.expertSessionEvents(input2.sessionId)) ?? []);
41388
+ const journal = ExpertSessionTransactionJournalV10Schema.parse({
41389
+ schemaVersion: "pragma.expert-session-transaction/v10",
41390
+ session: recovered,
41391
+ prompts,
41392
+ events: materializeSessionEvents(input2.sessionId, events, [
41393
+ {
41394
+ eventId: `context-recovered:${session.rootContextId}:${recovered.updatedAt}`,
41395
+ type: "context.recovered",
41396
+ data: { contextId: session.rootContextId, reason: input2.reason },
41397
+ occurredAt: recovered.updatedAt
41398
+ },
41399
+ {
41400
+ eventId: `session-recovered:${recovered.updatedAt}`,
41401
+ type: "session.recovered",
41402
+ data: { reason: input2.reason },
41403
+ occurredAt: recovered.updatedAt
41404
+ }
41405
+ ])
41406
+ });
41407
+ await writeJson(paths.expertSessionTransaction(input2.sessionId), journal);
41408
+ await applyTransaction2(paths, options2.executions, input2.sessionId, journal);
41409
+ await writeJson(paths.expertSessionLease(input2.sessionId), {
41410
+ claimId: input2.claimId,
41411
+ processId: process.pid,
41412
+ expiresAt: new Date(Date.now() + input2.leaseMs).toISOString()
41413
+ });
41414
+ return recovered;
41415
+ });
41416
+ },
41280
41417
  async transact(sessionId, action) {
41281
41418
  return await withFileLock(paths.expertSessionLock(sessionId), async () => {
41282
41419
  await prepareExpertSession(paths, options2.executions, sessionId);
@@ -41622,7 +41759,8 @@ function createPragma(options2) {
41622
41759
  return {
41623
41760
  experts: {
41624
41761
  createSession: async (expert, request2) => await experts.createSession(expert, request2),
41625
- resumeSession: async (expert, request2) => await experts.resumeSession(expert, request2)
41762
+ resumeSession: async (expert, request2) => await experts.resumeSession(expert, request2),
41763
+ recoverClosedSession: async (expert, request2) => await experts.recoverClosedSession(expert, request2)
41626
41764
  },
41627
41765
  flows: {
41628
41766
  start: async (flow, request2) => await flows.start(flow, request2),
@@ -424490,7 +424628,7 @@ init_dist13();
424490
424628
  init_integration();
424491
424629
 
424492
424630
  // apps/cli/src/version.ts
424493
- var CLI_VERSION = false ? "0.0.0" : "0.1.0-next.14";
424631
+ var CLI_VERSION = false ? "0.0.0" : "0.1.0-next.15";
424494
424632
 
424495
424633
  // apps/cli/src/composition/default.ts
424496
424634
  function createCliLocalHost(input2 = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pqpo/pragma",
3
- "version": "0.1.0-next.14",
3
+ "version": "0.1.0-next.15",
4
4
  "description": "Pragma command-line interface",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",