@trydreamstate/cli 1.3.7 → 1.3.11

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 (56) hide show
  1. package/agent-plugin/.claude-plugin/plugin.json +1 -1
  2. package/agent-plugin/.codex-plugin/plugin.json +1 -1
  3. package/agent-plugin/skills/crm/SKILL.md +2 -0
  4. package/agent-plugin/skills/diagnostics/SKILL.md +7 -1
  5. package/agent-plugin/skills/facebook-posts/SKILL.md +46 -0
  6. package/agent-plugin/skills/instagram-posts/SKILL.md +56 -0
  7. package/agent-plugin/skills/list-building/SKILL.md +3 -0
  8. package/agent-plugin/skills/list-building/column-authoring.md +6 -0
  9. package/agent-plugin/skills/list-building/incoming-people.md +32 -0
  10. package/agent-plugin/skills/list-building/sourcing.md +14 -0
  11. package/agent-plugin/skills/outreach/SKILL.md +8 -0
  12. package/agent-plugin/skills/outreach/enrollment.md +5 -1
  13. package/agent-plugin/skills/plays/SKILL.md +15 -0
  14. package/agent-plugin/skills/social/SKILL.md +4 -0
  15. package/agent-plugin/skills/tiktok-posts/SKILL.md +51 -0
  16. package/agent-plugin/skills/visibility/SKILL.md +4 -0
  17. package/agent-plugin/skills/workflows/SKILL.md +2 -0
  18. package/agent-plugin/skills/workflows/lifecycle-and-enrollment.md +2 -0
  19. package/agent-plugin/skills/youtube-posts/SKILL.md +49 -0
  20. package/dist/client.d.ts +6 -1
  21. package/dist/client.d.ts.map +1 -1
  22. package/dist/client.js +35 -11
  23. package/dist/commandTable.d.ts +2 -2
  24. package/dist/commandTable.d.ts.map +1 -1
  25. package/dist/commandTable.js +5 -2
  26. package/dist/commands/genericPack.d.ts +13 -0
  27. package/dist/commands/genericPack.d.ts.map +1 -0
  28. package/dist/commands/genericPack.js +31 -0
  29. package/dist/commands/knowledge.d.ts +2 -0
  30. package/dist/commands/knowledge.d.ts.map +1 -0
  31. package/dist/commands/knowledge.js +6 -0
  32. package/dist/commands/shared.d.ts +3 -1
  33. package/dist/commands/shared.d.ts.map +1 -1
  34. package/dist/commands/shared.js +14 -5
  35. package/dist/commands/tables.d.ts +2 -0
  36. package/dist/commands/tables.d.ts.map +1 -0
  37. package/dist/commands/tables.js +6 -0
  38. package/dist/commands/unibox.d.ts +2 -0
  39. package/dist/commands/unibox.d.ts.map +1 -0
  40. package/dist/commands/unibox.js +6 -0
  41. package/dist/gatedRun.d.ts.map +1 -1
  42. package/dist/gatedRun.js +105 -35
  43. package/dist/generated/packVerbs.generated.json +332 -4
  44. package/dist/index.d.ts +2 -1
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +47 -6
  47. package/dist/money.d.ts +2 -1
  48. package/dist/money.d.ts.map +1 -1
  49. package/dist/money.js +22 -5
  50. package/dist/runWait.d.ts +1 -1
  51. package/dist/runWait.d.ts.map +1 -1
  52. package/dist/runWait.js +37 -5
  53. package/dist/turnCache.d.ts +5 -1
  54. package/dist/turnCache.d.ts.map +1 -1
  55. package/dist/turnCache.js +5 -3
  56. package/package.json +1 -1
@@ -6,8 +6,8 @@
6
6
  * what the CLI really parses, with no second hand-kept list:
7
7
  *
8
8
  * - every `dreamstate ...` usage line the CLI prints (USAGE in index.ts), and
9
- * - every sequences / workflows verb (the verb tables in commands/), with the
10
- * capability each one runs.
9
+ * - every sequences / workflows / knowledge / unibox / tables verb (the verb
10
+ * tables in commands/), with the capability each one runs.
11
11
  *
12
12
  * A capability with no verb is reached through `tools run`, which stays a
13
13
  * catalog search (`tools search`); `commands search` reports those separately.
@@ -1 +1 @@
1
- {"version":3,"file":"commandTable.d.ts","sourceRoot":"","sources":["../src/commandTable.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AA6BD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE,CAWhE;AAMD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,eAAe,EAAE,CAc5F"}
1
+ {"version":3,"file":"commandTable.d.ts","sourceRoot":"","sources":["../src/commandTable.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AA6BD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE,CAchE;AAMD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,eAAe,EAAE,CAc5F"}
@@ -11,7 +11,7 @@ function usageEntries(usage) {
11
11
  return usage.split('\n')
12
12
  .filter((line) => line.startsWith(' dreamstate '))
13
13
  .map((line) => line.trim())
14
- .filter((line) => !/^dreamstate (sequences|workflows) <verb>/.test(line))
14
+ .filter((line) => !/^dreamstate (sequences|workflows|knowledge|unibox|tables) <verb>/.test(line))
15
15
  .map((line) => {
16
16
  const words = line.split(/\s+/);
17
17
  // `dreamstate tools run <capability_id> ...`: the typed command is the leading words
@@ -25,13 +25,16 @@ function cliCommandTable(usage) {
25
25
  // Required lazily: the verb tables import the CLI entry point's helpers.
26
26
  const { SEQUENCE_VERBS } = require('./commands/sequences');
27
27
  const { WORKFLOW_VERBS } = require('./commands/workflows');
28
+ const { KNOWLEDGE_VERBS } = require('./commands/knowledge');
29
+ const { UNIBOX_VERBS } = require('./commands/unibox');
30
+ const { TABLES_VERBS } = require('./commands/tables');
28
31
  const verbs = (pack, specs) => specs.map((spec) => ({
29
32
  command: `dreamstate ${pack} ${spec.verb}`,
30
33
  usage: verbUsage(pack, spec),
31
34
  summary: '',
32
35
  capability_id: spec.capabilityId,
33
36
  }));
34
- return [...usageEntries(usage), ...verbs('sequences', SEQUENCE_VERBS), ...verbs('workflows', WORKFLOW_VERBS)];
37
+ return [...usageEntries(usage), ...verbs('sequences', SEQUENCE_VERBS), ...verbs('workflows', WORKFLOW_VERBS), ...verbs('knowledge', KNOWLEDGE_VERBS), ...verbs('unibox', UNIBOX_VERBS), ...verbs('tables', TABLES_VERBS)];
35
38
  }
36
39
  function terms(value) {
37
40
  return value.toLowerCase().split(/[^a-z0-9_]+/).filter((word) => word.length > 1);
@@ -0,0 +1,13 @@
1
+ import { type PackVerbSpec } from './shared';
2
+ import type { CliCommand } from '../index';
3
+ import type { GatedRunContext } from '../gatedRun';
4
+ /** Pack groups that have no shared authoring kind and no local init scaffold. */
5
+ export type SimplePackName = 'knowledge' | 'unibox' | 'tables';
6
+ export interface PackSurface {
7
+ verbs: PackVerbSpec[];
8
+ usage: string;
9
+ parse: (argv: string[]) => CliCommand;
10
+ run: (context: GatedRunContext | undefined, command: CliCommand, stdout: NodeJS.WritableStream) => Promise<void>;
11
+ }
12
+ export declare function packSurface(pack: SimplePackName): PackSurface;
13
+ //# sourceMappingURL=genericPack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"genericPack.d.ts","sourceRoot":"","sources":["../../src/commands/genericPack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoD,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAC/F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAInD,iFAAiF;AACjF,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE/D,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,UAAU,CAAC;IACtC,GAAG,EAAE,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClH;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,cAAc,GAAG,WAAW,CAoB7D"}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.packSurface = packSurface;
7
+ const shared_1 = require("./shared");
8
+ const errors_1 = require("../errors");
9
+ const packVerbs_generated_json_1 = __importDefault(require("../generated/packVerbs.generated.json"));
10
+ function packSurface(pack) {
11
+ const prefix = pack + ' ';
12
+ const verbs = packVerbs_generated_json_1.default.verbs
13
+ .filter(({ command }) => command.startsWith(prefix))
14
+ .map(({ command, capabilityId, positional, local }) => ({
15
+ verb: command.slice(prefix.length),
16
+ capabilityId,
17
+ ...(positional ? { positional } : {}),
18
+ ...(local === 'init' ? { local: 'init' } : {}),
19
+ }));
20
+ const usage = (0, shared_1.renderPackUsage)(pack, verbs);
21
+ return {
22
+ verbs,
23
+ usage,
24
+ parse: (0, shared_1.buildPackParser)(pack, undefined, verbs, usage),
25
+ async run(context, command, stdout) {
26
+ if (!context)
27
+ throw new errors_1.CliUsageError(pack + ' commands require an authenticated session.');
28
+ await (0, shared_1.runPackCommand)(context, command, stdout);
29
+ },
30
+ };
31
+ }
@@ -0,0 +1,2 @@
1
+ export declare const KNOWLEDGE_VERBS: import("./shared").PackVerbSpec[], KNOWLEDGE_USAGE: string, parseKnowledgeCommand: (argv: string[]) => import("..").CliCommand, runKnowledgeCommand: (context: import("../gatedRun").GatedRunContext | undefined, command: import("..").CliCommand, stdout: NodeJS.WritableStream) => Promise<void>;
2
+ //# sourceMappingURL=knowledge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knowledge.d.ts","sourceRoot":"","sources":["../../src/commands/knowledge.ts"],"names":[],"mappings":"AAEA,eAAO,MACE,eAAe,qCACf,eAAe,UACf,qBAAqB,+CACvB,mBAAmB,gJACE,CAAC"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ var _a;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.runKnowledgeCommand = exports.parseKnowledgeCommand = exports.KNOWLEDGE_USAGE = exports.KNOWLEDGE_VERBS = void 0;
5
+ const genericPack_1 = require("./genericPack");
6
+ _a = (0, genericPack_1.packSurface)('knowledge'), exports.KNOWLEDGE_VERBS = _a.verbs, exports.KNOWLEDGE_USAGE = _a.usage, exports.parseKnowledgeCommand = _a.parse, exports.runKnowledgeCommand = _a.run;
@@ -1,5 +1,7 @@
1
1
  import { type CliCommand } from '../index';
2
2
  import { type GatedRunContext } from '../gatedRun';
3
+ /** First-class CLI pack groups. sequences and workflows also carry the shared authoring kind. */
4
+ export type PackName = 'sequences' | 'workflows' | 'knowledge' | 'unibox' | 'tables';
3
5
  export interface PackVerbSpec {
4
6
  verb: string;
5
7
  capabilityId: string;
@@ -18,7 +20,7 @@ export interface PackVerbSpec {
18
20
  /** Local-only route; `init` accepts --name and --file without an API schema. */
19
21
  local?: 'init';
20
22
  }
21
- export declare function buildPackParser(pack: 'sequences' | 'workflows', kind: 'sequence' | 'workflow', specs: PackVerbSpec[], usage: string): (argv: string[]) => CliCommand;
23
+ export declare function buildPackParser(pack: PackName, kind: 'sequence' | 'workflow' | undefined, specs: PackVerbSpec[], usage: string): (argv: string[]) => CliCommand;
22
24
  /**
23
25
  * A starter document written to disk, so authoring can begin from a file the way the
24
26
  * rest of this pack now accepts one. It writes nothing to the workspace and calls no
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/commands/shared.ts"],"names":[],"mappings":"AAKA,OAAO,EAAc,KAAK,UAAU,EAAiC,MAAM,UAAU,CAAC;AAEtF,OAAO,EAA4B,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAS7E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IACnC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAClE,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA4CD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,WAAW,GAAC,WAAW,EAAE,IAAI,EAAE,UAAU,GAAC,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,MAAM,GAC/F,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,UAAU,CAsFhC;AAGD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKjG;AAED,wBAAgB,WAAW,CACzB,IAAI,EAAE,UAAU,GAAG,UAAU,EAC7B,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,CAAC,cAAc,GAC5B,IAAI,CAuBN;AAYD,wBAAsB,cAAc,CAClC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,CAAC,cAAc,GAC5B,OAAO,CAAC,IAAI,CAAC,CAoDf;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,CAK3E"}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/commands/shared.ts"],"names":[],"mappings":"AAKA,OAAO,EAAc,KAAK,UAAU,EAAiC,MAAM,UAAU,CAAC;AAEtF,OAAO,EAA4B,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAS7E,iGAAiG;AACjG,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAErF,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IACnC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAClE,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA4CD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,GAAC,UAAU,GAAC,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,MAAM,GAC1F,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,UAAU,CAsFhC;AAGD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKjG;AAED,wBAAgB,WAAW,CACzB,IAAI,EAAE,UAAU,GAAG,UAAU,EAC7B,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,CAAC,cAAc,GAC5B,IAAI,CAuBN;AAYD,wBAAsB,cAAc,CAClC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,CAAC,cAAc,GAC5B,OAAO,CAAC,IAAI,CAAC,CA6Df;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,CAK3E"}
@@ -132,7 +132,7 @@ function buildPackParser(pack, kind, specs, usage) {
132
132
  // family-specific capabilities have strict schemas that do not declare it,
133
133
  // so injecting it everywhere made every one of those verbs fail validation
134
134
  // with an unrecognized key before it reached the server's real work.
135
- if (spec.capabilityId.startsWith('workflow.automations.'))
135
+ if (kind !== undefined && spec.capabilityId.startsWith('workflow.automations.'))
136
136
  input.kind = kind;
137
137
  // No default mode. A mutating verb run with no mode used to be rewritten to dry_run
138
138
  // silently, where `tools run` sends none and lets the server answer mode_required
@@ -238,10 +238,7 @@ async function runPackCommand(context, command, stdout) {
238
238
  const outcome = await (0, gatedRun_1.runGated)(context, {
239
239
  capabilityId: command.capabilityId,
240
240
  body: {
241
- params: {
242
- ...command.input,
243
- ...(command.maxLiveSends === undefined ? {} : { max_live_sends: command.maxLiveSends }),
244
- },
241
+ params: command.input,
245
242
  ...(command.mode ? { mode: command.mode } : {}),
246
243
  ...(maxCredits === undefined ? {} : { max_credits: maxCredits }),
247
244
  ...(command.approved ? { approved: true } : {}),
@@ -254,6 +251,18 @@ async function runPackCommand(context, command, stdout) {
254
251
  waitTimeout: command.waitTimeout,
255
252
  ...(command.noWait ? { noWait: true } : {}),
256
253
  coerceParams: true,
254
+ // max_live_sends is a capability param, not a door control. It belongs in
255
+ // params only where the live schema declares it; sequence lifecycle and
256
+ // enrollment capabilities (for example sequence.sequences.activate_audience)
257
+ // have a strict, cap-free schema, so injecting it there was refused before
258
+ // the request left the CLI.
259
+ ...(command.maxLiveSends === undefined ? {} : { adjustParams: (params, capability) => {
260
+ const schema = capability?.input_json_schema;
261
+ const properties = schema && typeof schema === 'object' ? schema.properties : null;
262
+ return properties && typeof properties === 'object' && Object.prototype.hasOwnProperty.call(properties, 'max_live_sends')
263
+ ? { ...params, max_live_sends: command.maxLiveSends }
264
+ : params;
265
+ } }),
257
266
  });
258
267
  const result = outcome.value;
259
268
  if (outcome.timedOut)
@@ -0,0 +1,2 @@
1
+ export declare const TABLES_VERBS: import("./shared").PackVerbSpec[], TABLES_USAGE: string, parseTablesCommand: (argv: string[]) => import("..").CliCommand, runTablesCommand: (context: import("../gatedRun").GatedRunContext | undefined, command: import("..").CliCommand, stdout: NodeJS.WritableStream) => Promise<void>;
2
+ //# sourceMappingURL=tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../src/commands/tables.ts"],"names":[],"mappings":"AAEA,eAAO,MACE,YAAY,qCACZ,YAAY,UACZ,kBAAkB,+CACpB,gBAAgB,gJACE,CAAC"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ var _a;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.runTablesCommand = exports.parseTablesCommand = exports.TABLES_USAGE = exports.TABLES_VERBS = void 0;
5
+ const genericPack_1 = require("./genericPack");
6
+ _a = (0, genericPack_1.packSurface)('tables'), exports.TABLES_VERBS = _a.verbs, exports.TABLES_USAGE = _a.usage, exports.parseTablesCommand = _a.parse, exports.runTablesCommand = _a.run;
@@ -0,0 +1,2 @@
1
+ export declare const UNIBOX_VERBS: import("./shared").PackVerbSpec[], UNIBOX_USAGE: string, parseUniboxCommand: (argv: string[]) => import("..").CliCommand, runUniboxCommand: (context: import("../gatedRun").GatedRunContext | undefined, command: import("..").CliCommand, stdout: NodeJS.WritableStream) => Promise<void>;
2
+ //# sourceMappingURL=unibox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unibox.d.ts","sourceRoot":"","sources":["../../src/commands/unibox.ts"],"names":[],"mappings":"AAEA,eAAO,MACE,YAAY,qCACZ,YAAY,UACZ,kBAAkB,+CACpB,gBAAgB,gJACE,CAAC"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ var _a;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.runUniboxCommand = exports.parseUniboxCommand = exports.UNIBOX_USAGE = exports.UNIBOX_VERBS = void 0;
5
+ const genericPack_1 = require("./genericPack");
6
+ _a = (0, genericPack_1.packSurface)('unibox'), exports.UNIBOX_VERBS = _a.verbs, exports.UNIBOX_USAGE = _a.usage, exports.parseUniboxCommand = _a.parse, exports.runUniboxCommand = _a.run;
@@ -1 +1 @@
1
- {"version":3,"file":"gatedRun.d.ts","sourceRoot":"","sources":["../src/gatedRun.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gatedRun.d.ts","sourceRoot":"","sources":["../src/gatedRun.ts"],"names":[],"mappings":"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"}
package/dist/gatedRun.js CHANGED
@@ -121,7 +121,19 @@ function planFingerprint(body) {
121
121
  delete params[key];
122
122
  return (0, node_crypto_1.createHash)('sha256').update((0, idempotency_1.stableJson)({ params, max_credits: body.max_credits ?? null })).digest('hex');
123
123
  }
124
- /** The quote a dry run returned, wherever the envelope nests its outputs. */
124
+ function creditsPhrase(estimate) {
125
+ if (!estimate)
126
+ return null;
127
+ const { minimum_credits: minimum, maximum_credits: maximum, expected_credits: expected } = estimate;
128
+ if (typeof minimum === 'number' && typeof maximum === 'number' && minimum !== maximum)
129
+ return `${minimum}..${maximum} credits`;
130
+ if (typeof minimum !== 'number' && typeof maximum === 'number')
131
+ return `up to ${maximum} credits`;
132
+ if (typeof expected === 'number')
133
+ return `${expected} credits`;
134
+ return typeof maximum === 'number' ? `${maximum} credits` : null;
135
+ }
136
+ /** The quote a supported rehearsal returned, wherever the envelope nests its outputs. */
125
137
  function quoteFromResult(value, depth = 0) {
126
138
  if (!value || typeof value !== 'object' || depth > 6)
127
139
  return null;
@@ -135,10 +147,14 @@ function quoteFromResult(value, depth = 0) {
135
147
  }
136
148
  const entry = value;
137
149
  if (typeof entry.quote_hash === 'string' && runContract_1.QUOTE_HASH_PATTERN.test(entry.quote_hash)) {
138
- const estimate = entry.estimate && typeof entry.estimate === 'object' ? entry.estimate : null;
150
+ const estimate = record(entry.credit_estimate) ?? record(entry.estimate);
151
+ const affected = record(entry.affected_count);
152
+ const rows = affected?.rows ?? affected?.row_or_recipient_count;
139
153
  return {
140
154
  quote_hash: entry.quote_hash,
141
155
  maximum_credits: typeof estimate?.maximum_credits === 'number' ? estimate.maximum_credits : null,
156
+ rows: typeof rows === 'number' ? rows : null,
157
+ credits: creditsPhrase(estimate),
142
158
  };
143
159
  }
144
160
  for (const key of ['data', 'run', 'outputs', 'steps']) {
@@ -148,11 +164,20 @@ function quoteFromResult(value, depth = 0) {
148
164
  }
149
165
  return null;
150
166
  }
151
- function quotePriced(capabilityId, body, quote) {
167
+ function quotePriced(capabilityId, body, price, rehearsal = 'dry_run') {
152
168
  const params = body.params && typeof body.params === 'object' ? body.params : {};
153
- const rows = typeof params.max_rows === 'number' ? `${params.max_rows} rows` : 'rows not stated';
154
- const credits = quote.maximum_credits === null ? 'credits not stated' : `up to ${quote.maximum_credits} credits`;
155
- return `Dry run of ${capabilityId} priced: ${rows}, ${credits}.`;
169
+ const rowCount = price.rows ?? (typeof params.max_rows === 'number' ? params.max_rows : null);
170
+ const rows = rowCount === null ? 'rows not stated' : `${rowCount} rows`;
171
+ const credits = price.credits ?? 'credits not stated';
172
+ const name = rehearsal === 'smoke_test' ? 'Smoke test' : rehearsal === 'dry_run' ? 'Dry run' : 'Rehearsal';
173
+ return `${name} of ${capabilityId} priced: ${rows}, ${credits}.`;
174
+ }
175
+ function rehearsalMode(fetched) {
176
+ const contract = record(capabilityRecord(fetched)?.execution_mode_contract);
177
+ const supported = Array.isArray(contract?.supported_modes) ? contract.supported_modes : [];
178
+ if (supported.includes('dry_run'))
179
+ return 'dry_run';
180
+ return supported.includes('smoke_test') ? 'smoke_test' : null;
156
181
  }
157
182
  const CREDENTIALS_REQUIRED_SENTENCE = 'A connected credential is required before this run can continue.';
158
183
  function readinessRecord(value) {
@@ -315,10 +340,10 @@ async function runGated(context, input) {
315
340
  if (params)
316
341
  body.params = params;
317
342
  }
318
- // A paid live run needs the quote_hash of a dry run of the same input,
319
- // and a quote is bound to the turn that asked for it. So a turn is reused
320
- // only while a quote is pending, together with the quote its dry run
321
- // returned, and forgotten once the paid run is admitted.
343
+ // A quote-bound live run needs the quote_hash of a supported rehearsal of
344
+ // the same input, and the quote is bound to the turn that asked for it. So a
345
+ // turn is reused only while a quote is pending, together with that quote,
346
+ // and forgotten once the live run is admitted.
322
347
  const explicitKey = parameterIdempotencyKey(body) ?? input.idempotencyKey;
323
348
  const fingerprint = planFingerprint(body);
324
349
  const cached = input.turnToken
@@ -332,7 +357,7 @@ async function runGated(context, input) {
332
357
  const inlineFromCache = !input.turnToken && !pending && !explicitKey && cachedPosture?.inlineRead === true;
333
358
  if (!input.turnToken && !pending && !inlineFromCache)
334
359
  await contract();
335
- const posture = fetched ? (0, capabilityPosture_1.postureFromContract)(fetched) : cachedPosture;
360
+ let posture = fetched ? (0, capabilityPosture_1.postureFromContract)(fetched) : cachedPosture;
336
361
  if (fetched && posture)
337
362
  (0, registryManifest_1.rememberPosture)(capabilityId, posture);
338
363
  const refusal = fetched ? cliSurfaceRefusal(capabilityId, fetched) : null;
@@ -346,9 +371,12 @@ async function runGated(context, input) {
346
371
  let quoteHash = explicitQuote
347
372
  ? body.quote_hash
348
373
  : live ? cached?.quote_hash ?? undefined : undefined;
349
- // null means the price was not stated (or the flag named a hash with no ceiling).
350
- let quotedMaximum = !explicitQuote && cached?.quote_hash ? cached.maximum_credits : undefined;
351
- let ranDryRun = false;
374
+ // The price of the quote in hand. A cached quote prices this input only when it
375
+ // is the quote named (explicit --quote-hash) or the quote this input was dry run for.
376
+ let quotedPrice = cached?.quote_hash && (!explicitQuote || cached.quote_hash === body.quote_hash)
377
+ ? { quote_hash: cached.quote_hash, maximum_credits: cached.maximum_credits, rows: cached.rows ?? null, credits: cached.credits ?? null }
378
+ : null;
379
+ let ranRehearsal = false;
352
380
  let sentKey = null;
353
381
  const requestBody = () => ({
354
382
  ...body,
@@ -379,20 +407,48 @@ async function runGated(context, input) {
379
407
  sentKey = await keyFor(sent);
380
408
  return client.runCapability(capabilityId, sent, sentKey);
381
409
  };
410
+ let retriedCapabilityDigestStale = false;
411
+ const refreshCapabilityAfterDigestStale = async () => {
412
+ retriedCapabilityDigestStale = true;
413
+ // A stale refusal is terminal for the original logical attempt. A new
414
+ // contract starts a fresh generated key, while a caller-supplied key keeps
415
+ // its explicit replay semantics.
416
+ if (attemptIdentity) {
417
+ await (0, idempotency_1.finishIdempotencyAttempt)(idempotencyPath, attemptIdentity);
418
+ attemptIdentity = null;
419
+ }
420
+ if (!input.turnToken) {
421
+ await (0, turnCache_1.forgetTurnToken)(context.turnsPath, context.baseUrl, context.turnKey, capabilityId, warnTurnCache);
422
+ }
423
+ fetched = await client.getTool(capabilityId);
424
+ posture = (0, capabilityPosture_1.postureFromContract)(fetched);
425
+ (0, registryManifest_1.rememberPosture)(capabilityId, posture);
426
+ const refreshedTurnToken = optionalToolTurnToken(fetched);
427
+ turnToken = refreshedTurnToken;
428
+ // A cached quote is bound to the stale turn. An explicitly supplied quote
429
+ // remains caller intent and the server decides whether it is still valid.
430
+ if (!explicitQuote) {
431
+ quoteHash = undefined;
432
+ quotedPrice = null;
433
+ }
434
+ };
382
435
  let result;
383
436
  try {
384
437
  try {
385
438
  if (live && quoteHash) {
386
- const maximum = explicitQuote ? null : quotedMaximum ?? null;
387
439
  // The shared gateway owns per-call paid, external-effect and recurring
388
440
  // consent. A positive quote alone does not require human approval.
389
441
  // Forward only caller-supplied approval; preserve every server refusal.
390
- stderr.write(`${quotePriced(capabilityId, body, { quote_hash: quoteHash, maximum_credits: maximum })}\nRunning live on that quote.\n`);
442
+ stderr.write(`${quotePriced(capabilityId, body, quotedPrice ?? { rows: null, credits: null }, rehearsalMode(fetched))}\nRunning live on that quote.\n`);
391
443
  }
392
444
  result = await run();
393
445
  }
394
446
  catch (error) {
395
- if (!input.turnToken && code(error) === 'capability_not_fetched') {
447
+ if (code(error) === 'capability_digest_stale') {
448
+ await refreshCapabilityAfterDigestStale();
449
+ result = await run();
450
+ }
451
+ else if (!input.turnToken && code(error) === 'capability_not_fetched') {
396
452
  // Capability posture can change between tools get and run. Refresh it
397
453
  // once rather than maintaining a client-side list of token-free reads.
398
454
  fetched = await client.getTool(capabilityId);
@@ -403,17 +459,18 @@ async function runGated(context, input) {
403
459
  result = await run();
404
460
  }
405
461
  else if (live && code(error) === 'quote_required' && !quoteHash && turnToken) {
406
- // A live paid run with no quote behind it. The refusal's own quote is
407
- // only a price and is never replayed, so price the same input with a
408
- // real dry run (free, never calls a paid provider), show the person
409
- // its rows and cost, and run live on that quote. Above the size or
410
- // dollar threshold the server still refuses until --approved.
411
- const dry = await client.runCapability(capabilityId, {
462
+ // A live quote is minted by the capability's advertised rehearsal.
463
+ // Most capabilities use dry_run; activation intentionally has only
464
+ // smoke_test and live, so never invent an unsupported dry_run mode.
465
+ const rehearsal = rehearsalMode(fetched);
466
+ if (!rehearsal)
467
+ throw error;
468
+ const rehearsalResult = await client.runCapability(capabilityId, {
412
469
  ...body,
413
- mode: 'dry_run',
470
+ mode: rehearsal,
414
471
  tool_turn_token: turnToken,
415
472
  }, null);
416
- const quote = quoteFromResult(dry);
473
+ const quote = quoteFromResult(rehearsalResult);
417
474
  if (!quote)
418
475
  throw error;
419
476
  // The no-quote call was definitively refused before execution. Its
@@ -422,11 +479,11 @@ async function runGated(context, input) {
422
479
  await (0, idempotency_1.finishIdempotencyAttempt)(idempotencyPath, attemptIdentity);
423
480
  attemptIdentity = null;
424
481
  }
425
- ranDryRun = true;
482
+ ranRehearsal = true;
426
483
  quoteHash = quote.quote_hash;
427
- quotedMaximum = quote.maximum_credits;
428
- stderr.write(`${quotePriced(capabilityId, body, quote)}\n`);
429
- await (0, turnCache_1.rememberTurnToken)(context.turnsPath, context.baseUrl, context.turnKey, capabilityId, turnToken, Date.now(), warnTurnCache, { quote_hash: quote.quote_hash, fingerprint, maximum_credits: quote.maximum_credits });
484
+ quotedPrice = quote;
485
+ stderr.write(`${quotePriced(capabilityId, body, quote, rehearsal)}\n`);
486
+ await (0, turnCache_1.rememberTurnToken)(context.turnsPath, context.baseUrl, context.turnKey, capabilityId, turnToken, Date.now(), warnTurnCache, { quote_hash: quote.quote_hash, fingerprint, maximum_credits: quote.maximum_credits, rows: quote.rows, credits: quote.credits });
430
487
  stderr.write('Running live on that quote.\n');
431
488
  result = await run();
432
489
  }
@@ -442,19 +499,22 @@ async function runGated(context, input) {
442
499
  if (code(error) === 'approval_required') {
443
500
  stderr.write('This run needs a person\'s approval. Show them the rows and cost above; when they agree, rerun the same command with --approved.\n');
444
501
  }
445
- await (0, turnCache_1.rememberTurnToken)(context.turnsPath, context.baseUrl, context.turnKey, capabilityId, turnToken, Date.now(), warnTurnCache, quoteHash ? { quote_hash: quoteHash, fingerprint, ...(quotedMaximum !== undefined ? { maximum_credits: quotedMaximum } : {}) } : undefined);
502
+ await (0, turnCache_1.rememberTurnToken)(context.turnsPath, context.baseUrl, context.turnKey, capabilityId, turnToken, Date.now(), warnTurnCache, quoteHash ? { quote_hash: quoteHash, fingerprint, maximum_credits: quotedPrice?.maximum_credits ?? null, rows: quotedPrice?.rows ?? null, credits: quotedPrice?.credits ?? null } : undefined);
446
503
  }
447
504
  // Only an unknown or transient result may resume an attempt. A server
448
505
  // refusal with retryable:false is terminal and must receive a new key.
449
- const transient = error instanceof client_1.CliError && (error.code === 'request_timeout' || error.code === 'network_error' || error.retryable === true || error.status >= 500);
506
+ // A response body that cannot be parsed is an unknown-effect outcome: the
507
+ // server may have admitted the write before its reply was damaged. Keep the
508
+ // generated key so the unchanged retry can replay that exact attempt.
509
+ const transient = error instanceof client_1.CliError && (error.code === 'request_timeout' || error.code === 'network_error' || error.code === 'invalid_response' || error.retryable === true || error.status >= 500);
450
510
  if (attemptIdentity && !transient)
451
511
  await (0, idempotency_1.finishIdempotencyAttempt)(idempotencyPath, attemptIdentity);
452
512
  throw error;
453
513
  }
454
514
  if (!input.turnToken) {
455
- if (body.mode === 'dry_run' && turnToken) {
515
+ if ((body.mode === 'dry_run' || body.mode === 'smoke_test') && turnToken) {
456
516
  const quote = quoteFromResult(result);
457
- await (0, turnCache_1.rememberTurnToken)(context.turnsPath, context.baseUrl, context.turnKey, capabilityId, turnToken, Date.now(), warnTurnCache, quote ? { quote_hash: quote.quote_hash, fingerprint, maximum_credits: quote.maximum_credits } : undefined);
517
+ await (0, turnCache_1.rememberTurnToken)(context.turnsPath, context.baseUrl, context.turnKey, capabilityId, turnToken, Date.now(), warnTurnCache, quote ? { quote_hash: quote.quote_hash, fingerprint, maximum_credits: quote.maximum_credits, rows: quote.rows, credits: quote.credits } : undefined);
458
518
  }
459
519
  }
460
520
  let timedOut = false;
@@ -463,16 +523,26 @@ async function runGated(context, input) {
463
523
  result = waited.value;
464
524
  timedOut = waited.timedOut;
465
525
  }
526
+ let outcomeRefusal = refusalFromRunResult(result);
527
+ if (!retriedCapabilityDigestStale && code(outcomeRefusal) === 'capability_digest_stale') {
528
+ await refreshCapabilityAfterDigestStale();
529
+ result = await run();
530
+ if (!input.noWait && !posture?.returnsRunId) {
531
+ const waited = await (0, runWait_1.waitForRun)(client, result, input.waitTimeout, context.sleep);
532
+ result = waited.value;
533
+ timedOut = waited.timedOut;
534
+ }
535
+ outcomeRefusal = refusalFromRunResult(result);
536
+ }
466
537
  // A timeout still owns the quote and the idempotency key. Forgetting the
467
538
  // turn here made the rerun hash a body with no quote_hash and start a second run.
468
- if (!input.turnToken && body.mode !== 'dry_run' && (pending || ranDryRun) && !timedOut) {
539
+ if (!input.turnToken && body.mode !== 'dry_run' && body.mode !== 'smoke_test' && (pending || ranRehearsal) && !timedOut) {
469
540
  await (0, turnCache_1.forgetTurnToken)(context.turnsPath, context.baseUrl, context.turnKey, capabilityId, warnTurnCache);
470
541
  }
471
542
  // A run still active after waiting has unknown outcome; retain its key for
472
543
  // the read-back/retry rule. A terminal response completes this attempt.
473
544
  if (attemptIdentity && !timedOut)
474
545
  await (0, idempotency_1.finishIdempotencyAttempt)(idempotencyPath, attemptIdentity);
475
- const outcomeRefusal = refusalFromRunResult(result);
476
546
  if (outcomeRefusal)
477
547
  throw outcomeRefusal;
478
548
  return { value: withIdempotencyMeta(result, explicitKey ? null : sentKey), timedOut, idempotencyKey: sentKey, turnToken: turnToken ?? null };