@proagentstore/cli 0.4.127 → 0.4.129
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/browser-runner/local-apply/runtime.js +4 -3
- package/dist/browser-runner/local-artifact/contract.js +7 -1
- package/dist/browser-runner/local-artifact/engine.js +8 -0
- package/dist/browser-runner/local-artifact/runtime.js +21 -2
- package/dist/browser-runner/local-browser/runtime.js +5 -2
- package/dist/browser-runner/runner.js +3 -3
- package/package.json +1 -1
|
@@ -129,7 +129,7 @@ export function parseApplyEnvelope(raw) {
|
|
|
129
129
|
workspace: o.workspace,
|
|
130
130
|
sources,
|
|
131
131
|
artifacts,
|
|
132
|
-
policy: { mode: p.mode, allowDomains, ...(p.mode === "auto_submit" && gateId ? { submitGate: { gateId } } : {}) },
|
|
132
|
+
policy: { mode: p.mode, allowDomains, ...(p.mode === "auto_submit" && gateId ? { submitGate: { gateId } } : {}), ...(p.screenshots === false ? { screenshots: false } : {}) },
|
|
133
133
|
limits,
|
|
134
134
|
};
|
|
135
135
|
}
|
|
@@ -310,7 +310,8 @@ export class LocalApplyRuntime {
|
|
|
310
310
|
token: randomBytes(24).toString("hex"),
|
|
311
311
|
state: "running",
|
|
312
312
|
events: [],
|
|
313
|
-
|
|
313
|
+
// Screenshots off (#1064): zero cap for `/shots`, and no recorder handed to the bridge below.
|
|
314
|
+
shots: new ShotRecorder(envelope.policy.screenshots === false ? 0 : APPLY_SHOT_CAP, () => this.now()),
|
|
314
315
|
seq: 0,
|
|
315
316
|
allow: new Set(envelope.policy.allowDomains),
|
|
316
317
|
grounding: [],
|
|
@@ -396,7 +397,7 @@ export class LocalApplyRuntime {
|
|
|
396
397
|
limits: e.limits,
|
|
397
398
|
mode: e.policy.mode,
|
|
398
399
|
gateId: e.policy.submitGate?.gateId,
|
|
399
|
-
}, run.shots);
|
|
400
|
+
}, e.policy.screenshots === false ? undefined : run.shots);
|
|
400
401
|
if (spec.mcpConfig)
|
|
401
402
|
writeFileSync(spec.mcpConfig.path, spec.mcpConfig.json, { mode: 0o600 });
|
|
402
403
|
const child = this.spawn(spec.command, spec.args, { cwd: join(run.dir, "scratch"), env: spec.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -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 {
|
|
@@ -104,6 +104,7 @@ export function parseEnvelope(raw) {
|
|
|
104
104
|
consentedDomains: strList(p.consentedDomains),
|
|
105
105
|
profileConsented: p.profileConsented === true,
|
|
106
106
|
consentIds: consentIdMap(p.consentIds),
|
|
107
|
+
...(p.screenshots === false ? { screenshots: false } : {}),
|
|
107
108
|
},
|
|
108
109
|
limits,
|
|
109
110
|
resultSchema: { id: typeof schema.id === "string" ? schema.id : "findings", version: typeof schema.version === "number" ? schema.version : 1 },
|
|
@@ -185,7 +186,9 @@ export class LocalBrowserRuntime {
|
|
|
185
186
|
token: randomBytes(24).toString("hex"),
|
|
186
187
|
state: "running",
|
|
187
188
|
events: [],
|
|
188
|
-
|
|
189
|
+
// Screenshots off (#1064): a zero-cap recorder, so `/shots` answers with nothing — and the bridge
|
|
190
|
+
// below is given NO recorder at all, so no frame is ever attempted.
|
|
191
|
+
shots: new ShotRecorder(envelope.policy.screenshots === false ? 0 : SCOUT_SHOT_CAP, () => this.now()),
|
|
189
192
|
seq: 0,
|
|
190
193
|
consented: new Set(envelope.policy.consentedDomains),
|
|
191
194
|
deny: new Set(envelope.policy.denyDomains),
|
|
@@ -230,7 +233,7 @@ export class LocalBrowserRuntime {
|
|
|
230
233
|
allowListOnly: () => e.policy.allowDomains.length > 0,
|
|
231
234
|
overTime: () => this.activeMs(run) >= e.limits.maxMinutes * 60_000,
|
|
232
235
|
limits: e.limits,
|
|
233
|
-
}, run.shots);
|
|
236
|
+
}, e.policy.screenshots === false ? undefined : run.shots);
|
|
234
237
|
const bridgeScript = this.deps.bridgeScript ?? fileURLToPath(new URL("./bridge-stdio.js", import.meta.url));
|
|
235
238
|
const spec = buildEngineSpec({
|
|
236
239
|
engine: e.engine,
|
|
@@ -1109,7 +1109,7 @@ export class LocalRunner {
|
|
|
1109
1109
|
url: active.url(),
|
|
1110
1110
|
title: await active.title().catch(() => ""),
|
|
1111
1111
|
challenge: await detectHumanChallenge(active),
|
|
1112
|
-
screenshot: await this.shot(active),
|
|
1112
|
+
screenshot: action.screenshot === false ? undefined : await this.shot(active),
|
|
1113
1113
|
commitGuard: { supported: true, mode: guard?.mode },
|
|
1114
1114
|
};
|
|
1115
1115
|
}
|
|
@@ -1143,7 +1143,7 @@ export class LocalRunner {
|
|
|
1143
1143
|
title: await settled.title().catch(() => ""),
|
|
1144
1144
|
challenge: await detectHumanChallenge(settled),
|
|
1145
1145
|
feedback: dialogMsg ? `a native dialog was accepted: "${dialogMsg}"` : "a native dialog was accepted",
|
|
1146
|
-
screenshot: await this.shot(settled),
|
|
1146
|
+
screenshot: action.screenshot === false ? undefined : await this.shot(settled),
|
|
1147
1147
|
commitGuard: { supported: true, mode: guard?.mode },
|
|
1148
1148
|
};
|
|
1149
1149
|
}
|
|
@@ -1177,7 +1177,7 @@ export class LocalRunner {
|
|
|
1177
1177
|
title: await active.title().catch(() => ""),
|
|
1178
1178
|
challenge: await detectHumanChallenge(active),
|
|
1179
1179
|
feedback: feedback || undefined,
|
|
1180
|
-
screenshot: await this.shot(active),
|
|
1180
|
+
screenshot: action.screenshot === false ? undefined : await this.shot(active),
|
|
1181
1181
|
commitGuard: { supported: true, mode: guard?.mode },
|
|
1182
1182
|
};
|
|
1183
1183
|
}
|