@proagentstore/cli 0.4.127 → 0.4.128

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.
@@ -28,6 +28,12 @@ export const LOCAL_ARTIFACT_ENGINES = ["claude", "codex"];
28
28
  export const LOCAL_ARTIFACT_AUTH_MODES = ["machine", "subscription"];
29
29
  export const LOCAL_ARTIFACT_SOURCE_ROLES = ["resume", "profile", "answers"];
30
30
  export const REQUIRED_SOURCE_ROLES = ["resume", "profile"];
31
+ /**
32
+ * The owner's saved answers to earlier blocking questions (#1066), sent by PAGS from the owner's
33
+ * Profile and given to the CLI as one more citable source under this role. Not a file source: it
34
+ * never appears in `sources`, `sourceHashes` or a path, and an older runner simply ignores it.
35
+ */
36
+ export const OWNER_ANSWERS_ROLE = "owner_answers";
31
37
  export const LOCAL_ARTIFACT_BLOCK_REASONS = [
32
38
  "missing_source",
33
39
  "malformed_lead",
@@ -52,7 +58,7 @@ export const LOCAL_ARTIFACT_EVENT_TYPES = [
52
58
  ];
53
59
  export const LOCAL_ARTIFACT_ENGINE_AUTHS = ["machine-login", "subscription", "api-key", "missing_login", "unknown"];
54
60
  export const LOCAL_ARTIFACT_PARSE_ATTEMPTS = ["fenced_json", "balanced_object"];
55
- export const LOCAL_ARTIFACT_CAPS = { questions: 20, questionChars: 300, sources: 6, sourceBytes: 256 * 1024 };
61
+ export const LOCAL_ARTIFACT_CAPS = { questions: 20, questionChars: 300, sources: 6, sourceBytes: 256 * 1024, ownerAnswers: 30, ownerAnswerChars: 500 };
56
62
  /** An id used as a folder name: no separators, no dots, nothing a path could be built out of. */
57
63
  export const PATH_SEGMENT = /^[A-Za-z0-9_-]{1,100}$/;
58
64
  /** A source path inside the workspace: relative, no `..`, no absolute, no NUL, no backslash. */
@@ -20,6 +20,7 @@
20
20
  import { mergeEnv } from "../coding/engine-env.js";
21
21
  import { resolveEngineAuth } from "../coding/engine-auth.js";
22
22
  import { finalText as finalTextFromEngine, missingLogin } from "../local-browser/engine.js";
23
+ import { OWNER_ANSWERS_ROLE } from "./contract.js";
23
24
  export { missingLogin };
24
25
  const PROVIDER_KEYS = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY"];
25
26
  const SUBSCRIPTION_TOKEN = "CLAUDE_CODE_OAUTH_TOKEN";
@@ -51,6 +52,10 @@ export function buildArtifactEngineSpec(input) {
51
52
  };
52
53
  }
53
54
  /** The brief. The rules are stated, but the checks in `checkDraft` are what enforce them. */
55
+ /** The owner's saved answers as one source text: a question and its answer per pair (#1066). */
56
+ export function ownerAnswersText(answers) {
57
+ return answers.map((a) => `Q: ${a.question}\nA: ${a.answer}`).join("\n\n");
58
+ }
54
59
  export function tailorPrompt(lead, sources) {
55
60
  const l = lead.lead;
56
61
  const job = [
@@ -78,6 +83,9 @@ export function tailorPrompt(lead, sources) {
78
83
  '- For EVERY factual claim about the owner in either document, add an entry to "claims" with the claim, the source role it comes from, and a short VERBATIM quote copied exactly from that source.',
79
84
  "- Every year, email address and phone number you write must appear in the sources.",
80
85
  "- Reword and reorder to fit the job; do not add what the sources do not say. Leave out what does not fit.",
86
+ ...(sources.some((x) => x.role === OWNER_ANSWERS_ROLE)
87
+ ? [`- <source role="${OWNER_ANSWERS_ROLE}"> holds the owner's own answers to questions asked on earlier applications. They are the owner's facts and preferences: use them, cite them like any source, and do not ask again a question they already answer.`]
88
+ : []),
81
89
  '- If something the application needs is missing or uncertain, do not guess: answer {"status":"needs_human","questions":["…"]} instead.',
82
90
  "",
83
91
  "Answer shape when you can do it:",
@@ -28,8 +28,8 @@ import { join, resolve, sep } from "node:path";
28
28
  import { RunnerInputError } from "../errors.js";
29
29
  import { redactText, secretEnvValues } from "../local-browser/contract.js";
30
30
  import { resolveWorkspacePath } from "../local-browser/runtime.js";
31
- import { LOCAL_ARTIFACT_AUTH_MODES, LOCAL_ARTIFACT_CAPS, LOCAL_ARTIFACT_ENGINES, LOCAL_ARTIFACT_SOURCE_ROLES, LOCAL_ARTIFACT_TASK_TYPE, PATH_SEGMENT, REQUIRED_SOURCE_ROLES, isHomeRelative, isWorkspaceRelative, parseLocalArtifactLead, } from "./contract.js";
32
- import { buildArtifactEngineSpec, checkDraft, finalText, missingLogin, observedArtifactAuth, parseDraft, tailorPrompt } from "./engine.js";
31
+ import { LOCAL_ARTIFACT_AUTH_MODES, LOCAL_ARTIFACT_CAPS, OWNER_ANSWERS_ROLE, LOCAL_ARTIFACT_ENGINES, LOCAL_ARTIFACT_SOURCE_ROLES, LOCAL_ARTIFACT_TASK_TYPE, PATH_SEGMENT, REQUIRED_SOURCE_ROLES, isHomeRelative, isWorkspaceRelative, parseLocalArtifactLead, } from "./contract.js";
32
+ import { buildArtifactEngineSpec, checkDraft, finalText, missingLogin, observedArtifactAuth, ownerAnswersText, parseDraft, tailorPrompt } from "./engine.js";
33
33
  /** The manifest in every generated folder — what retention reads, and proof the folder is ours. */
34
34
  export const ARTIFACT_MANIFEST = ".pags-artifact.json";
35
35
  const MAX_EVENTS = 200;
@@ -87,6 +87,18 @@ export function parseArtifactEnvelope(raw) {
87
87
  if (uploadedSources.length !== 2 || !uploadedSources.some((s) => s.role === "resume") || !uploadedSources.some((s) => s.role === "profile"))
88
88
  throw bad("uploaded sources need one resume and one profile");
89
89
  }
90
+ // The owner's saved answers to earlier questions (#1066): optional, bounded, plain text only.
91
+ const ownerAnswers = [];
92
+ if (o.ownerAnswers !== undefined) {
93
+ if (!Array.isArray(o.ownerAnswers) || o.ownerAnswers.length > LOCAL_ARTIFACT_CAPS.ownerAnswers)
94
+ throw bad(`ownerAnswers must be a list of at most ${LOCAL_ARTIFACT_CAPS.ownerAnswers}`);
95
+ for (const a of o.ownerAnswers) {
96
+ const r = (a && typeof a === "object" ? a : {});
97
+ if (typeof r.question !== "string" || !r.question.trim() || r.question.length > LOCAL_ARTIFACT_CAPS.questionChars || typeof r.answer !== "string" || !r.answer.trim() || r.answer.length > LOCAL_ARTIFACT_CAPS.ownerAnswerChars)
98
+ throw bad("each owner answer needs a question and an answer within their caps");
99
+ ownerAnswers.push({ question: r.question.trim(), answer: r.answer.trim() });
100
+ }
101
+ }
90
102
  const p = (o.policy && typeof o.policy === "object" ? o.policy : {});
91
103
  const retainDays = intIn(p.retainDays, 0, 3650);
92
104
  const maxMinutes = intIn(p.maxMinutes, 1, 60);
@@ -104,6 +116,7 @@ export function parseArtifactEnvelope(raw) {
104
116
  workspace: o.workspace,
105
117
  sources,
106
118
  ...(uploadedSources.length ? { uploadedSources } : {}),
119
+ ...(ownerAnswers.length ? { ownerAnswers } : {}),
107
120
  policy: { retainDays, maxMinutes, maxConcurrent },
108
121
  ...("lead" in lead ? { lead: lead.lead } : { lead: null, leadError: lead.error }),
109
122
  };
@@ -235,6 +248,12 @@ export class LocalArtifactRuntime {
235
248
  }
236
249
  if (missing.length)
237
250
  throw new Pause("missing_source", missing);
251
+ // The owner's saved answers go in as one more source the CLI may cite (#1066). Not a file, so no
252
+ // source hash or path; the count alone is recorded, never the text.
253
+ if (e.ownerAnswers?.length) {
254
+ texts.push({ role: OWNER_ANSWERS_ROLE, text: ownerAnswersText(e.ownerAnswers) });
255
+ this.emit(run, { type: "source.read", detail: { role: OWNER_ANSWERS_ROLE, count: e.ownerAnswers.length } });
256
+ }
238
257
  const outRel = `applications/${lead.leadId}/${e.runId}`;
239
258
  const outDir = join(workspace, "applications", lead.leadId, e.runId);
240
259
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proagentstore/cli",
3
- "version": "0.4.127",
3
+ "version": "0.4.128",
4
4
  "description": "CLI for creating, publishing, and running ProAgentStore agents",
5
5
  "license": "MIT",
6
6
  "type": "module",