trackly-cli 0.7.0 → 0.7.2
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/README.md +2 -1
- package/bin/trackly +50 -2
- package/contracts/trackly-apply-tools.json +6 -1
- package/docs/trackly-tools.md +2 -1
- package/lib/agent.js +2 -2
- package/mcp/server.js +27 -7
- package/package.json +2 -4
- package/server.json +2 -2
- package/skills/trackly-apply/SKILL.md +15 -6
- package/skills/trackly-apply/references/ats-playbook.md +1 -1
- package/skills/trackly-apply/references/form-integrity.md +5 -1
- package/skills/trackly-apply/references/review-handoff.md +1 -0
- package/skills/trackly-apply/references/scenario-coverage.md +5 -0
package/README.md
CHANGED
|
@@ -171,6 +171,7 @@ Then use natural language in any of these clients:
|
|
|
171
171
|
| trackly_get_profile_onboarding | Get schema plus missing questions |
|
|
172
172
|
| trackly_update_application_profile | Save scoped answers with optimistic concurrency |
|
|
173
173
|
| trackly_start_apply_run | Start a manual-submit browser run |
|
|
174
|
+
| trackly_get_apply_evidence | Get aggregate, value-free beta evidence and release readiness |
|
|
174
175
|
| trackly_get_apply_protocol | Get current workflow and compatibility rules |
|
|
175
176
|
| trackly_report_apply_observation | Send redacted ATS mechanics feedback |
|
|
176
177
|
| trackly_record_application_outcome | Record review or confirmed submission outcome |
|
|
@@ -249,7 +250,7 @@ Install trackly-cli (`npm install -g trackly-cli`), authenticate with `trackly l
|
|
|
249
250
|
|
|
250
251
|
**What MCP servers exist for job searching?**
|
|
251
252
|
|
|
252
|
-
trackly-cli includes a built-in MCP server with
|
|
253
|
+
trackly-cli includes a built-in MCP server with 22 tools for job search, company lookup, application tracking, profile onboarding, beta evidence, and manual-submit form preparation. Run `trackly mcp` or use `trackly agent setup --client claude`.
|
|
253
254
|
|
|
254
255
|
**How do I use Claude Code for job hunting?**
|
|
255
256
|
|
package/bin/trackly
CHANGED
|
@@ -39,6 +39,8 @@ const VALUE_FLAGS = new Set([
|
|
|
39
39
|
'sort',
|
|
40
40
|
'status',
|
|
41
41
|
'url',
|
|
42
|
+
'window-days',
|
|
43
|
+
'target-reviewed-runs',
|
|
42
44
|
]);
|
|
43
45
|
|
|
44
46
|
// Flags accepted on ANY command. `api-key`/`base-url` are global overrides consumed
|
|
@@ -75,7 +77,7 @@ const COMMAND_FLAGS = {
|
|
|
75
77
|
'request-company': ['url', 'notes'],
|
|
76
78
|
'api-key': ['name'],
|
|
77
79
|
config: ['api-key', 'base-url', 'clear-api-key', 'clear-base-url'],
|
|
78
|
-
agent: ['client'],
|
|
80
|
+
agent: ['client', 'window-days', 'target-reviewed-runs'],
|
|
79
81
|
};
|
|
80
82
|
const AUTH_HELP_TEXT = `Not authenticated.
|
|
81
83
|
Option 1: trackly login (Google OAuth)
|
|
@@ -995,7 +997,52 @@ async function cmdAgent(args) {
|
|
|
995
997
|
console.log(report.browserControl.note);
|
|
996
998
|
return;
|
|
997
999
|
}
|
|
998
|
-
|
|
1000
|
+
if (subcommand === 'evidence') {
|
|
1001
|
+
const parseBoundedInteger = (value, fallback, flag, maximum) => {
|
|
1002
|
+
if (value === undefined) return fallback;
|
|
1003
|
+
if (value === null || !/^\d+$/.test(String(value))) {
|
|
1004
|
+
failNow(`${flag} must be an integer from 1 to ${maximum}.`);
|
|
1005
|
+
}
|
|
1006
|
+
const parsed = Number(value);
|
|
1007
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > maximum) {
|
|
1008
|
+
failNow(`${flag} must be an integer from 1 to ${maximum}.`);
|
|
1009
|
+
}
|
|
1010
|
+
return parsed;
|
|
1011
|
+
};
|
|
1012
|
+
const windowDays = parseBoundedInteger(args['window-days'], 90, '--window-days', 365);
|
|
1013
|
+
const targetReviewedRuns = parseBoundedInteger(
|
|
1014
|
+
args['target-reviewed-runs'],
|
|
1015
|
+
20,
|
|
1016
|
+
'--target-reviewed-runs',
|
|
1017
|
+
1000,
|
|
1018
|
+
);
|
|
1019
|
+
try {
|
|
1020
|
+
const result = await apiRequest(
|
|
1021
|
+
'GET',
|
|
1022
|
+
`/api/jobscout/apply/evidence?windowDays=${windowDays}&targetReviewedRuns=${targetReviewedRuns}`,
|
|
1023
|
+
);
|
|
1024
|
+
if (isJSON()) return printJson(result);
|
|
1025
|
+
const report = result.report;
|
|
1026
|
+
console.log(color('bold', 'Trackly Apply evidence'));
|
|
1027
|
+
console.log(`Window: last ${report.windowDays} days`);
|
|
1028
|
+
console.log(`Reviewed: ${report.summary.reviewedRuns}/${report.targetReviewedRuns}; submitted=${report.summary.submittedRuns}; blocked=${report.summary.blockedRuns}`);
|
|
1029
|
+
if (report.summary.excludedLegacyRuns) {
|
|
1030
|
+
console.log(`Excluded pre-evidence runs: ${report.summary.excludedLegacyRuns}`);
|
|
1031
|
+
}
|
|
1032
|
+
console.log(`Critical contact integrity: ${report.evidence.criticalContact.coveragePercent}% (${report.evidence.criticalContact.verifiedRuns}/${report.summary.reviewedRuns}); corrected=${report.evidence.criticalContact.correctedRuns}; unresolved=${report.evidence.criticalContact.unresolvedFailureRuns}`);
|
|
1033
|
+
console.log(`Manual-submit boundary: ${report.evidence.manualSubmitBoundary.coveragePercent}% (${report.evidence.manualSubmitBoundary.verifiedRuns}/${report.summary.reviewedRuns}); violations=${report.evidence.manualSubmitBoundary.violationRuns}`);
|
|
1034
|
+
for (const provider of report.providers) {
|
|
1035
|
+
console.log(`${provider.provider}: reviewed=${provider.reviewedRuns}; submitted=${provider.submittedRuns}; blocked=${provider.blockedRuns}`);
|
|
1036
|
+
}
|
|
1037
|
+
console.log(`Release gate: ${color(report.releaseGate.ready ? 'green' : 'yellow', report.releaseGate.ready ? 'ready' : 'not ready')}`);
|
|
1038
|
+
if (report.releaseGate.blockers.length) console.log(`Blockers: ${report.releaseGate.blockers.join(', ')}`);
|
|
1039
|
+
return;
|
|
1040
|
+
} catch (error) {
|
|
1041
|
+
setApiErrorExit(error, 'Failed to fetch Trackly Apply evidence');
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
failNow('Unknown agent subcommand. Use: trackly agent setup --client codex|claude|both | trackly agent doctor | trackly agent evidence');
|
|
999
1046
|
}
|
|
1000
1047
|
|
|
1001
1048
|
function showHelp() {
|
|
@@ -1036,6 +1083,7 @@ ${color('bold', 'Commands:')}
|
|
|
1036
1083
|
${color('cyan', 'mcp')} Start MCP server
|
|
1037
1084
|
${color('cyan', 'agent setup')} Install Trackly Apply for Codex/Claude (--client codex|claude|both)
|
|
1038
1085
|
${color('cyan', 'agent doctor')} Verify auth, MCP, skill, profile, resume, and compatibility
|
|
1086
|
+
${color('cyan', 'agent evidence')} Show value-free beta evidence (--window-days, --target-reviewed-runs)
|
|
1039
1087
|
|
|
1040
1088
|
${color('bold', 'Flags:')}
|
|
1041
1089
|
--api-key <key> Use an API key for this command
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
{
|
|
2
|
-
"contractVersion": "3.
|
|
2
|
+
"contractVersion": "3.2.0",
|
|
3
|
+
"constants": {
|
|
4
|
+
"applyScenarioCodes": ["browser_reclaim", "resume_upload", "resume_parser_recheck", "semantic_boolean_commit", "custom_select_commit", "multi_step_navigation", "free_text_voice", "required_error_sweep", "final_consent", "handoff_reclaim", "critical_contact_integrity", "manual_submit_boundary", "job_identity_match"],
|
|
5
|
+
"applyBrowserSurfaces": ["codex_in_app", "chrome_extension", "claude_in_chrome"]
|
|
6
|
+
},
|
|
3
7
|
"tools": {
|
|
4
8
|
"trackly_get_apply_queue": "{limit:z.number().int().min(1).max(100).optional()}",
|
|
5
9
|
"trackly_get_application_profile": "{includeSensitive:z.boolean().optional(),provider:z.string().max(100).optional(),companyId:z.string().max(100).optional()}",
|
|
6
10
|
"trackly_get_profile_onboarding": "{}",
|
|
7
11
|
"trackly_update_application_profile": "{expectedRevision:z.number().int().min(1),source:z.enum(['web','ios','macos','codex','claude','mcp']).optional(),changes:z.array(z.discriminatedUnion('scope',[z.object({key:z.string().min(1).max(200),state:z.enum(['unknown','answered','intentionally_blank','declined']),value:z.any().optional(),scope:z.literal('global'),questionLabel:z.string().max(1000).optional()}),z.object({key:z.string().min(1).max(200),state:z.enum(['unknown','answered','intentionally_blank','declined']),value:z.any().optional(),scope:z.literal('provider'),scopeValue:z.string().min(1).max(200),questionLabel:z.string().max(1000).optional()}),z.object({key:z.string().min(1).max(200),state:z.enum(['unknown','answered','intentionally_blank','declined']),value:z.any().optional(),scope:z.literal('company'),scopeValue:z.string().min(1).max(200),questionLabel:z.string().max(1000).optional()})])).max(100).optional(),education:z.array(z.object({school:z.string().min(1).max(500),degree:z.string().max(500).nullable().optional(),fieldOfStudy:z.string().max(500).nullable().optional(),gpa:z.string().max(50).nullable().optional(),startDate:z.string().max(50).nullable().optional(),endDate:z.string().max(50).nullable().optional()})).max(20).optional(),confirmProfile:z.boolean().optional(),sensitiveStorageConsent:z.boolean().optional()}",
|
|
8
12
|
"trackly_start_apply_run": "{jobId:z.number().int().min(1),clientName:z.string().max(100).optional()}",
|
|
13
|
+
"trackly_get_apply_evidence": "{windowDays:z.number().int().min(1).max(365).optional(),targetReviewedRuns:z.number().int().min(1).max(1000).optional()}",
|
|
9
14
|
"trackly_get_apply_protocol": "{}",
|
|
10
15
|
"trackly_report_apply_observation": "{runId:z.number().int().min(1),provider:z.string().regex(SAFE_OBSERVATION_CODE),fieldLabel:z.string().min(1).max(1000),observationType:z.string().regex(SAFE_OBSERVATION_CODE),resolutionCode:z.string().regex(SAFE_OBSERVATION_CODE).optional(),metadata:z.object({controlType:z.string().regex(SAFE_OBSERVATION_CODE).optional(),required:z.boolean().optional(),errorCode:z.string().regex(SAFE_OBSERVATION_CODE).optional(),committed:z.boolean(),scenarioCode:z.enum(APPLY_SCENARIO_CODES),browserSurface:z.enum(APPLY_BROWSER_SURFACES),browserBindingHash:z.string().regex(/^[a-f0-9]{64}$/).optional(),resumedAfterHandoff:z.boolean().optional()})}",
|
|
11
16
|
"trackly_record_application_outcome": "{runId:z.number().int().min(1),outcome:z.enum(['review_ready','submitted','failed','blocked']),confirmation:z.string().max(500).optional()}",
|
package/docs/trackly-tools.md
CHANGED
|
@@ -93,13 +93,14 @@ You can also pass `TRACKLY_API_KEY` as an environment variable for one-off runs.
|
|
|
93
93
|
- **trackly_get_profile_onboarding** — Get backend-owned questions plus only missing/unconfirmed answers.
|
|
94
94
|
- **trackly_update_application_profile** — Save explicit answer states with optimistic concurrency and global/provider/company scope.
|
|
95
95
|
- **trackly_start_apply_run** — Start a manual-submit browser run for a queued job.
|
|
96
|
+
- **trackly_get_apply_evidence** — Get the authenticated user's aggregate beta evidence and release gate without returning profile answers or contact values.
|
|
96
97
|
- **trackly_get_apply_protocol** — Get the current ATS support, browser integrity rules, and compatible skill version.
|
|
97
98
|
- **trackly_report_apply_observation** — Report redacted ATS mechanics and actual scenario coverage without answer values or page text.
|
|
98
99
|
- **trackly_record_application_outcome** — Record review readiness or a confirmed manual submission.
|
|
99
100
|
- **trackly_prepare_resume** — Local MCP only: materialize the default resume in a private, expiring mode-0600 cache and return filename, size, SHA-256, exact local path, and visual-confirmation metadata. Hosted MCP returns a manual/local-agent requirement.
|
|
100
101
|
- **trackly_verify_prepared_resume** — Local MCP only: immediately before attachment, recompute the user-confirmed resume hash and size, validate the exact path/run/expiration, and lock the file read-only. Any mismatch requires a fresh preview and confirmation.
|
|
101
102
|
|
|
102
|
-
Apply contract v3 intentionally gives this verifier different local and hosted schemas: local MCP receives the full proof needed to inspect the private file, while hosted MCP accepts only run and confirmation identifiers and returns the manual/local-agent requirement. Local paths and fingerprints are never sent to the hosted verifier. Version 3 also
|
|
103
|
+
Apply contract v3 intentionally gives this verifier different local and hosted schemas: local MCP receives the full proof needed to inspect the private file, while hosted MCP accepts only run and confirmation identifiers and returns the manual/local-agent requirement. Local paths and fingerprints are never sent to the hosted verifier. Version 3.1 also records universal value-free evidence for critical-contact integrity and the manual-submit boundary. Version 3.2 authorizes the exact stored HTTPS origin for jobs Trackly ingested from employer careers sources, without granting redirect, iframe, or hostname-suffix privileges.
|
|
103
104
|
|
|
104
105
|
### Maintenance behavior
|
|
105
106
|
|
package/lib/agent.js
CHANGED
|
@@ -7,13 +7,13 @@ const path = require('node:path');
|
|
|
7
7
|
const { spawnSync } = require('node:child_process');
|
|
8
8
|
const { apiRequest, downloadFile, getConfigPaths, hasAuth, maintenanceOutput } = require('./client');
|
|
9
9
|
const { version: PACKAGE_VERSION } = require('../package.json');
|
|
10
|
-
const SKILL_VERSION = '4.
|
|
10
|
+
const SKILL_VERSION = '4.1.1';
|
|
11
11
|
const CLI_USER_AGENT = `trackly-cli/${PACKAGE_VERSION}`;
|
|
12
12
|
const MCP_USER_AGENT = `trackly-mcp/${PACKAGE_VERSION}`;
|
|
13
13
|
|
|
14
14
|
const SKILL_NAME = 'trackly-apply';
|
|
15
15
|
const SKILL_MAJOR = Number(SKILL_VERSION.split('.')[0]);
|
|
16
|
-
const MIN_APPLY_PROTOCOL_VERSION = '3.
|
|
16
|
+
const MIN_APPLY_PROTOCOL_VERSION = '3.1.0';
|
|
17
17
|
const CACHE_TTL_MS = 2 * 60 * 60 * 1000;
|
|
18
18
|
const MANAGED_FILE = '.trackly-managed.json';
|
|
19
19
|
const RESUME_PROOF_FILE = '.trackly-resume-proof.json';
|
package/mcp/server.js
CHANGED
|
@@ -7,16 +7,13 @@ const { z } = require('zod');
|
|
|
7
7
|
const { apiRequest, hasAuth, maintenanceOutput } = require('../lib/client');
|
|
8
8
|
const { prepareResume, verifyPreparedResume } = require('../lib/agent');
|
|
9
9
|
const { version: PACKAGE_VERSION } = require('../package.json');
|
|
10
|
+
const APPLY_CONTRACT = require('../contracts/trackly-apply-tools.json');
|
|
10
11
|
|
|
11
12
|
const MCP_USER_AGENT = `trackly-mcp/${PACKAGE_VERSION}`;
|
|
12
13
|
const MCP_MAINTENANCE_ERROR_CODE = -32002;
|
|
13
14
|
const AUTH_HINT = 'Run `trackly login` or set TRACKLY_API_KEY. Get a key at https://usetrackly.app (sign in → Settings → API Keys).';
|
|
14
|
-
const APPLY_BROWSER_SURFACES =
|
|
15
|
-
const APPLY_SCENARIO_CODES =
|
|
16
|
-
'browser_reclaim', 'resume_upload', 'resume_parser_recheck', 'semantic_boolean_commit',
|
|
17
|
-
'custom_select_commit', 'multi_step_navigation', 'free_text_voice',
|
|
18
|
-
'required_error_sweep', 'final_consent', 'handoff_reclaim',
|
|
19
|
-
];
|
|
15
|
+
const APPLY_BROWSER_SURFACES = APPLY_CONTRACT.constants.applyBrowserSurfaces;
|
|
16
|
+
const APPLY_SCENARIO_CODES = APPLY_CONTRACT.constants.applyScenarioCodes;
|
|
20
17
|
const SAFE_OBSERVATION_CODE = /^[a-z0-9][a-z0-9_:-]{0,99}$/;
|
|
21
18
|
|
|
22
19
|
// Mirrors `granola-followup-app/src/services/region-classifier.ts:8` REGION_TAGS.
|
|
@@ -437,6 +434,23 @@ function createServer() {
|
|
|
437
434
|
wrapTool(async (params) => apiRequest('POST', '/api/jobscout/apply/runs', params, false, false, MCP_USER_AGENT), 'Failed to start apply run')
|
|
438
435
|
);
|
|
439
436
|
|
|
437
|
+
server.tool(
|
|
438
|
+
'trackly_get_apply_evidence',
|
|
439
|
+
'Get the authenticated user\'s aggregate, value-free Apply beta evidence and release gate. The report never returns answers, contact values, addresses, or page text.',
|
|
440
|
+
{
|
|
441
|
+
windowDays: z.number().int().min(1).max(365).optional(),
|
|
442
|
+
targetReviewedRuns: z.number().int().min(1).max(1000).optional(),
|
|
443
|
+
},
|
|
444
|
+
wrapTool(async ({ windowDays, targetReviewedRuns }) => {
|
|
445
|
+
const qs = new URLSearchParams();
|
|
446
|
+
if (windowDays !== undefined) qs.set('windowDays', String(windowDays));
|
|
447
|
+
if (targetReviewedRuns !== undefined) qs.set('targetReviewedRuns', String(targetReviewedRuns));
|
|
448
|
+
const query = qs.toString();
|
|
449
|
+
const suffix = query ? `?${query}` : '';
|
|
450
|
+
return apiRequest('GET', `/api/jobscout/apply/evidence${suffix}`, null, false, false, MCP_USER_AGENT);
|
|
451
|
+
}, 'Failed to fetch apply evidence')
|
|
452
|
+
);
|
|
453
|
+
|
|
440
454
|
server.tool(
|
|
441
455
|
'trackly_get_apply_protocol',
|
|
442
456
|
'Get the current browser workflow, ATS support matrix, integrity rules, and compatible public-skill major version. Fetch at the start of every run and again after maintenance before resuming the existing run.',
|
|
@@ -512,7 +526,13 @@ function createServer() {
|
|
|
512
526
|
role: 'user',
|
|
513
527
|
content: {
|
|
514
528
|
type: 'text',
|
|
515
|
-
text: '
|
|
529
|
+
text: 'Compatibility gate: before starting a new run, require the fetched Trackly Apply protocol to be version 3.1.0 or newer. After trackly_start_apply_run returns, or before resuming an existing run, require the returned or stored run.protocolVersion to be version 3.1.0 or newer. Never continue or replace a pre-evidence 3.0.x run; preserve it, record it blocked when possible, and stop for supported lifecycle cleanup.',
|
|
530
|
+
},
|
|
531
|
+
}, {
|
|
532
|
+
role: 'user',
|
|
533
|
+
content: {
|
|
534
|
+
type: 'text',
|
|
535
|
+
text: 'Fetch the Trackly Apply protocol, profile onboarding, profile, and approved queue. Resolve missing answers with me. Treat required completeness separately from optional reusable coverage and employer-specific contextual questions. Before starting anything, stop on every non-null executionBlocker and every manual_only item. Start only the selected approved queue item, require major(run.protocolVersion) === major(protocol.version), require protocol.compatibleSkillMajor === 4, preserve the stored version for a resumed run, and require its provider, atsCapability, required scenarios, and originPolicy to match the queue preflight. Reclaim semantic browser control, verify the exact job/run/tab binding, hash that value-free binding, and report the same-run browser_ready attestation with committed=true. Before entering private data, require the visible company and role to match the run binding and, when available, the requisition identifier to match the stored job URL. When job_identity_match is required, report a value-free committed scenario_coverage attestation only after that visible identity check passes; never include the company, role, URL, requisition identifier, page text, or any profile value in that observation. On exact-origin fallback, revalidate the frozen company, role, and available requisition identity after every navigation or redirect and before entering any additional private data. Normalize every page, redirect, and data-receiving iframe URL; accept an exact authorized origin or hostname only when host === allowedDomain or host.endsWith("." + allowedDomain), never by substring or page text. When originPolicy.verification is trackly_employer_source_exact_origin, authorize only the exact origin in authorizedOrigins: never promote it to a host suffix and never carry it across a redirect or iframe origin change. For every other vendor-hosted ATS policy, require both originPolicy.tenantRule and originPolicy.verifiedAtsTenant to be non-null or stop before private data entry. Execute the backend-owned originPolicy.tenantRule exactly after every redirect or data-receiving iframe change, including its extraction, exact-host-depth, locale, percent-decoding, normalization, and fail-closed semantics, then require the normalized result to equal originPolicy.verifiedAtsTenant; never invent or reinterpret a strategy token. Obey every capability stop condition. Determine whether the form has a semantically identified Resume or CV attachment control. Only when that specific control exists, prepare the run-bound resume locally with that browser surface and binding hash, show me its exact path, filename, size, SHA-256, run, and expiration, and obtain my explicit confirmation. Treat cover-letter, portfolio, transcript, and other supporting-document controls separately according to the profile and protocol; never upload a resume to them. Immediately before attaching the resume, use the local verifier to validate the signed proof, recompute hash and size, check expiration, and lock the file read-only. Fill the form through semantic controls, verify committed values and every required error, and report a same-run passed or corrected scenario_coverage observation with committed=true for every backend-required scenario except browser_reclaim, which is satisfied only by browser_ready with the binding hash. Before every review_ready outcome, also report value-free committed critical_contact_integrity and manual_submit_boundary evidence; never include contact values, answers, page text, or local paths. If a required or universal review scenario cannot pass, record blocked rather than review_ready. Stop before Submit. If maintenance interrupts the run, retain the run and browser context, wait for the advertised window, refetch protocol, queue, and profile state, and resume the existing agent_browser run. Never start a duplicate run, blindly retry a mutation, enter credentials or verification codes, evade human verification, or click Submit.',
|
|
516
536
|
},
|
|
517
537
|
}],
|
|
518
538
|
}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trackly-cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"mcpName": "io.github.trackly-app/trackly",
|
|
5
5
|
"description": "AI job search CLI + hosted MCP server with OAuth. 128K+ jobs, 1,900+ companies, 40+ ATS. Works with Claude, ChatGPT, Cursor, Windsurf, Codex via hosted streamable-http or local stdio.",
|
|
6
6
|
"bin": {
|
|
@@ -58,11 +58,9 @@
|
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
61
|
+
"hono": "^4.12.30",
|
|
61
62
|
"zod": "^3.23.0"
|
|
62
63
|
},
|
|
63
|
-
"overrides": {
|
|
64
|
-
"hono": "^4.12.25"
|
|
65
|
-
},
|
|
66
64
|
"engines": {
|
|
67
65
|
"node": ">=18"
|
|
68
66
|
}
|
package/server.json
CHANGED
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
]
|
|
18
18
|
}
|
|
19
19
|
],
|
|
20
|
-
"version": "0.7.
|
|
20
|
+
"version": "0.7.2",
|
|
21
21
|
"packages": [
|
|
22
22
|
{
|
|
23
23
|
"registryType": "npm",
|
|
24
24
|
"identifier": "trackly-cli",
|
|
25
|
-
"version": "0.7.
|
|
25
|
+
"version": "0.7.2",
|
|
26
26
|
"runtimeHint": "npx",
|
|
27
27
|
"runtimeArguments": [
|
|
28
28
|
{
|
|
@@ -19,8 +19,8 @@ Use Trackly as the source of truth for profile answers, documents, queue decisio
|
|
|
19
19
|
|
|
20
20
|
## Start every run
|
|
21
21
|
|
|
22
|
-
1. Call `trackly_get_apply_protocol`. Skill 4.
|
|
23
|
-
2. Call `trackly_get_profile_onboarding` or fetch both the profile schema and application profile.
|
|
22
|
+
1. Call `trackly_get_apply_protocol`. Skill 4.1 requires protocol major 3 (version 3.1.0 or newer) and `compatibleSkillMajor: 4`. Reject an older or incompatible version and report that the backend must finish updating or `trackly agent setup` must update the skill. This gate prevents a pre-evidence skill or run from being reused under the stronger cross-ATS contract. Protocol 3.2 adds exact-origin trust for jobs Trackly ingested directly from employer careers sources; do not recreate the retired ownership-timestamp gate in the client.
|
|
23
|
+
2. Call `trackly_get_profile_onboarding` or fetch both the profile schema and application profile. Treat `completeness.percent` as required onboarding readiness only. Use `coverage.missingReusableKeys` to explain reusable optional gaps, while `coverage.contextualKeys` are intentionally asked only on the relevant employer form. Do not claim that 100% required completeness answers every possible application question.
|
|
24
24
|
3. Save answers with `trackly_update_application_profile`:
|
|
25
25
|
- Use `answered`, `intentionally_blank`, `declined`, or `unknown` exactly.
|
|
26
26
|
- If the user says “always,” save globally.
|
|
@@ -37,10 +37,11 @@ Use Trackly as the source of truth for profile answers, documents, queue decisio
|
|
|
37
37
|
5. Call `trackly_get_apply_queue`. Select deterministically unless the user names a job. Do not replace the queue call with a fresh job search.
|
|
38
38
|
- Use the queue item's backend-owned `atsCapability`, `originPolicy`, and `executionBlocker`. Never infer a stronger support claim from the page design or the LLM's familiarity with a provider. Required scenarios are backend-enforced gates; possible scenarios are reported only when actually exercised.
|
|
39
39
|
- Stop before `trackly_start_apply_run` whenever `executionBlocker` is non-null. `standard` runs use the provider playbook. `guided` runs are allowed only while semantic browser state remains observable, `originPolicy.authorized` is true, and every published stop condition is obeyed. `manual_only` items stop without starting or mutating a run.
|
|
40
|
+
- When `originPolicy.verification` is `trackly_employer_source_exact_origin`, Trackly has authorized the exact stored HTTPS origin from its direct employer-careers ingestion. Do not demand a separate ownership timestamp, ATS tenant, or company-domain suffix. This authorization never extends to another origin, hostname suffix, redirect, or iframe. The queue/run will require `job_identity_match`; pass it only after the browser visibly confirms the frozen company, role, and available requisition identity. Revalidate that identity after every navigation or redirect and before entering any additional private data.
|
|
40
41
|
- If the user requests the next `N` jobs, freeze the deterministic ordered set of exactly `N` job IDs before starting any run. Do not replace, rescore, or expand that approved batch.
|
|
41
42
|
- For each fixed batch member, preserve an explicit job ID -> application run ID -> browser tab mapping. Complete the full start -> conditional resume preparation/confirmation/verification when an upload control exists -> form completion -> `review_ready` lifecycle for every member. A review-ready run does not block the next member. Never submit any of them.
|
|
42
43
|
6. Call `trackly_start_apply_run` for the selected job or current fixed batch member. Reuse an active run returned by Trackly; never create a replacement run because browser control was interrupted.
|
|
43
|
-
- Require `run.protocolVersion` to share protocol major 3 with the fetched protocol.
|
|
44
|
+
- Require `run.protocolVersion` to be 3.1.0 or newer and to share protocol major 3 with the fetched protocol. Never continue a pre-evidence 3.0.x run under skill 4.1. Preserve that run instead of starting a replacement, record it `blocked` with a value-free protocol-upgrade reason when possible, and tell the user the saved job can be retried only after the stale run is cleared through Trackly's supported lifecycle. Compare `run.atsCapability` and `run.originPolicy` with the queue values. Stop and refetch the protocol and queue if support level, execution mode, provider, required scenarios, or authorized origin policy changed between preflight and run creation.
|
|
44
45
|
- In guided mode, inspect the page before preparing any resume bytes. Confirm the employer, role, HTTPS origin, reachable review path, semantic controls, whether an attachment control exists, and absence of a credential, verification, CAPTCHA, or submit-only wall. A missing file input is not itself a blocker; skip the resume path when the application has no attachment control. Any other failed precondition is an execution blocker, not permission to improvise.
|
|
45
46
|
7. Pass the browser readiness gate before preparing resume bytes:
|
|
46
47
|
- Use semantic browser control through Codex in-app browser controls, Chrome MCP/extension browser control, or Claude in Chrome.
|
|
@@ -78,7 +79,7 @@ Read [references/ats-playbook.md](references/ats-playbook.md) for the detected A
|
|
|
78
79
|
|
|
79
80
|
Follow this order:
|
|
80
81
|
|
|
81
|
-
1. Open the application in the controlled browser context and confirm the employer, role, ATS host, and HTTPS URL. Before entering private data,
|
|
82
|
+
1. Open the application in the controlled browser context and confirm the employer, role, ATS host, and HTTPS URL. Before entering private data, require the visible company and role to match the run binding and, when the stored URL exposes one, require the requisition identifier to match. When `job_identity_match` is required, immediately report its value-free committed `scenario_coverage` proof after this check passes; never include company, role, URL, requisition, or page text in the observation. On exact-origin fallback, revalidate the frozen company, role, and available requisition identity after every navigation or redirect and before entering any additional private data. Parse and normalize the URL, then require the exact origin to equal an `originPolicy.authorizedOrigins` entry or the normalized hostname to satisfy `host === allowedDomain` or `host.endsWith("." + allowedDomain)` for an allowed ATS suffix or verified company domain. Never use substring, display-text, logo, or suffix-without-a-dot matching. When `verification` is `trackly_employer_source_exact_origin`, accept only the exact listed origin; never convert it into a hostname suffix or carry it across a redirect or iframe origin change. For every other vendor-hosted ATS policy, require both `originPolicy.tenantRule` and `originPolicy.verifiedAtsTenant` to be non-null; stop before private data entry if either is missing. Execute the backend-owned declarative tenant rule exactly, including its extraction, exact-host-depth, locale, percent-decoding, normalization, and fail-closed semantics, then require the normalized result to equal `originPolicy.verifiedAtsTenant`; never invent or reinterpret a strategy token. Revalidate both origin and tenant after every redirect and apply the same policy to every iframe that receives private data. Stop on any unmatched origin or tenant, malformed percent encoding, missing required rule, or rule shape the client cannot execute exactly.
|
|
82
83
|
2. Inspect the whole form and identify required fields, semantic controls, consent controls, document inputs, and multi-step sections.
|
|
83
84
|
3. When an attachment control exists, only after the exact-hash visual confirmation and a successful immediate pre-attach `trackly_verify_prepared_resume` check, upload the prepared resume before autofill when parsing may overwrite contact fields. Do not change the file between verification and attachment. Verify that the filename chip exactly matches the prepared resume’s user-facing filename and contains no internal cache identifier. Stop and replace the attachment if it does not. When no attachment control exists, skip resume preparation and upload without treating that absence as an error.
|
|
84
85
|
4. Fill typed fields from the resolved Trackly profile. Clear parser-filled data when the canonical state is intentionally blank.
|
|
@@ -92,7 +93,15 @@ Follow this order:
|
|
|
92
93
|
|
|
93
94
|
When the user corrects an answer, immediately save the appropriate scope with `trackly_update_application_profile` and report only a redacted mechanics observation through `trackly_report_apply_observation`. Never promote one user’s value into a global default. For `generic_web_form`, never save provider-scoped answers; use company scope for form-specific answers.
|
|
94
95
|
|
|
95
|
-
For every run, track only scenarios actually exercised. Attest `browser_reclaim` once with the same-run `observationType: browser_ready`, exact binding hash, browser surface, and `metadata.committed: true`; do not send a duplicate `scenario_coverage` row for it. Before `review_ready`, report every other exercised scenario with `observationType: scenario_coverage`, the stable scenario code, browser surface, `metadata.committed: true` for `passed` or `corrected`, and whether the tab was resumed after handoff.
|
|
96
|
+
For every run, track only scenarios actually exercised, except the two universal review proofs below. Attest `browser_reclaim` once with the same-run `observationType: browser_ready`, exact binding hash, browser surface, and `metadata.committed: true`; do not send a duplicate `scenario_coverage` row for it. Before `review_ready`, report every other exercised scenario with `observationType: scenario_coverage`, the stable scenario code, browser surface, `metadata.committed: true` for `passed` or `corrected`, and whether the tab was resumed after handoff.
|
|
97
|
+
|
|
98
|
+
Always report both universal evidence scenarios before every `review_ready` outcome:
|
|
99
|
+
|
|
100
|
+
- `critical_contact_integrity`: inventory all email, phone, country-code, and other required contact controls; verify every present canonical field exactly after parsing/autofill; confirm no required contact control is omitted, duplicated, concatenated, placeholder-only, or visibly errored. If the form truly has no such control, pass only after the whole-form inventory proves none is required. Use `corrected` when any contact field needed repair.
|
|
101
|
+
- `manual_submit_boundary`: prove the live form is at its final review state, the Submit control is present or the ATS has an equivalent clearly identified boundary, and the agent did not activate it. A submit-only transition that cannot be inspected does not pass.
|
|
102
|
+
- `job_identity_match` (conditional): for exact-origin fallback runs, prove the visible company, role, and available requisition identity match the frozen Trackly job before entering private data. Report only value-free metadata.
|
|
103
|
+
|
|
104
|
+
Every required scenario and both universal evidence scenarios must have corresponding same-run committed evidence. If any is missing, uncommitted, or blocked, record the run as `blocked` instead of `review_ready`. Never include email, phone, applicant name, answer values, page text, or local paths in observations. Include the actual scenario coverage in the final handoff; do not claim unobserved coverage. Use `trackly_get_apply_evidence` or `trackly agent evidence` when the user asks for aggregate beta proof; never reconstruct a report from private chat content.
|
|
96
105
|
|
|
97
106
|
## Review handoff
|
|
98
107
|
|
|
@@ -112,5 +121,5 @@ After the user submits manually:
|
|
|
112
121
|
- `best_effort` means use the named playbook and stop if browser and accessibility state disagree.
|
|
113
122
|
- `guided` means the provider or employer-hosted page may be completed only through observable semantic controls. Obey every published stop condition; stop on login credentials, OTP/email verification, CAPTCHA/human verification, unexpected origin/employer, submit-only navigation, or unobservable committed state.
|
|
114
123
|
- `blocked` / `manual_only` means do not start or mutate an application. LinkedIn-hosted applications are manual-only. If Trackly already stores a separate external application URL, evaluate that stored URL as its own ATS/origin; do not request or invent a URL override that the run cannot bind.
|
|
115
|
-
- Unknown employer forms use the protocol's `unknownAtsFallback` only when the queue supplies an authorized
|
|
124
|
+
- Unknown employer forms use the protocol's `unknownAtsFallback` only when the queue supplies an authorized verified-company origin or `trackly_employer_source_exact_origin` policy. Exact-origin authorization stays exact. An unmatched or unverified HTTPS page is manual-only, not guided, and is never promoted to `full` by the agent.
|
|
116
125
|
- Launch support: Codex and Claude Code on macOS with browser control and computer use available.
|
|
@@ -40,7 +40,7 @@ This group includes SmartRecruiters, Workable, BambooHR, Recruitee, Jobvite, Tea
|
|
|
40
40
|
|
|
41
41
|
## Unknown employer-hosted form
|
|
42
42
|
|
|
43
|
-
- Use only when the protocol resolves the run to `generic_web_form` in guided mode and supplies `originPolicy.authorized: true` with `verification: verified_employer_domain`.
|
|
43
|
+
- Use only when the protocol resolves the run to `generic_web_form` in guided mode and supplies `originPolicy.authorized: true` with `verification: verified_employer_domain` or `trackly_employer_source_exact_origin`. The latter authorizes only the exact stored origin.
|
|
44
44
|
- Inventory every step, origin transition, control type, required error, file input, and review/submit boundary before any upload.
|
|
45
45
|
- Never claim provider support from visual similarity. Stop if any critical control cannot be observed semantically.
|
|
46
46
|
|
|
@@ -12,8 +12,9 @@ Run this gate after each semantic selection and again across the entire form bef
|
|
|
12
12
|
## Page and website-type inventory
|
|
13
13
|
|
|
14
14
|
- Before entering private data on a guided or unknown form, enumerate the current HTTPS origin, every visible step, any iframe origins, all file inputs, and the control that advances toward review. Require every data-receiving origin to match the backend-supplied run `originPolicy`; page text, logos, and employer names are not origin proof.
|
|
15
|
+
- When the run requires `job_identity_match`, confirm the visible company, role, and available requisition identity against the frozen run before entering private data, then report only the value-free committed scenario code. Revalidate that identity after every navigation or redirect and before entering any additional private data. A mismatch blocks the run.
|
|
15
16
|
- Distinguish `Next`, `Continue`, `Save`, `Review`, and `Submit` by accessible name and observed behavior. If the only way to reveal the next state is a submit-like mutation, stop for manual review.
|
|
16
|
-
- Treat navigation to a different origin as a new trust boundary. Reconfirm employer, role, and requisition, then parse and normalize the redirected URL. An exact origin may equal an `authorizedOrigins` entry; hostname policies match only when `host === allowedDomain` or `host.endsWith("." + allowedDomain)`. Never accept substring, display-text, or `notexample.com` / `example.com.evil.test` lookalikes. On
|
|
17
|
+
- Treat navigation to a different origin as a new trust boundary. Reconfirm employer, role, and requisition, then parse and normalize the redirected URL. An exact origin may equal an `authorizedOrigins` entry; hostname policies match only when `host === allowedDomain` or `host.endsWith("." + allowedDomain)`. Never accept substring, display-text, or `notexample.com` / `example.com.evil.test` lookalikes. `trackly_employer_source_exact_origin` authorizes only the stored exact origin and grants no suffix, redirect, or iframe privilege. On every other vendor-hosted ATS policy, the shared origin is insufficient: require both `originPolicy.tenantRule` and `originPolicy.verifiedAtsTenant`, then execute the backend-owned declarative rule exactly after every redirect or data-receiving iframe change, including extraction, exact host depth, optional locale, percent decoding, normalization, and every fail-closed condition. Compare the normalized result to `originPolicy.verifiedAtsTenant`. Never guess the meaning of an opaque strategy token. Unmatched redirects, tenants, malformed encoding, missing required or unexecutable rules, and iframe origins are manual-only.
|
|
17
18
|
- Treat an authentication wall as a user handoff. Never enter, retrieve, store, or infer credentials.
|
|
18
19
|
|
|
19
20
|
## Semantic controls
|
|
@@ -35,6 +36,7 @@ Run this gate after each semantic selection and again across the entire form bef
|
|
|
35
36
|
- Read both DOM input state and macOS accessibility state.
|
|
36
37
|
- Reject duplicate or concatenated values, autofill overlays, placeholder-only text, and values present only in a custom wrapper.
|
|
37
38
|
- Recheck when navigation, resume parsing, or a correction banner rerenders the form.
|
|
39
|
+
- Before review, inventory every email, phone, country-code, and required contact control. Pass `critical_contact_integrity` only when all present canonical values are exact and no required contact field was omitted. A form with no contact control passes only after the whole-form inventory confirms none exists or is required.
|
|
38
40
|
|
|
39
41
|
## Final sweep
|
|
40
42
|
|
|
@@ -45,6 +47,8 @@ Run this gate after each semantic selection and again across the entire form bef
|
|
|
45
47
|
5. Verify resume filename, education dates, links, and all critical contact fields again.
|
|
46
48
|
6. Confirm the Submit button is present but do not click it.
|
|
47
49
|
|
|
50
|
+
After the final sweep, pass `manual_submit_boundary` only when the live final-review state is observable and no Submit-equivalent control was activated. Report both universal evidence scenarios with value-free metadata before recording `review_ready`.
|
|
51
|
+
|
|
48
52
|
If any field’s committed state cannot be observed, fail the gate and explain which field needs manual review.
|
|
49
53
|
|
|
50
54
|
## Submission-state reconciliation
|
|
@@ -30,6 +30,11 @@ For each exercised scenario, call `trackly_report_apply_observation` with:
|
|
|
30
30
|
- `required_error_sweep`: every required field and visible error surface was checked.
|
|
31
31
|
- `final_consent`: the final certification, acknowledgement, or consent control was verified.
|
|
32
32
|
- `handoff_reclaim`: the browser tab was reclaimed after a context or control handoff.
|
|
33
|
+
- `critical_contact_integrity`: every present canonical contact control was exact after parsing/autofill and the form inventory proved that no required contact field was omitted.
|
|
34
|
+
- `manual_submit_boundary`: the agent reached an observable final review boundary and stopped without activating Submit or its equivalent.
|
|
35
|
+
- `job_identity_match`: on an exact-origin fallback run, the visible company, role, and available requisition identity matched the frozen Trackly job before private data entry.
|
|
36
|
+
|
|
37
|
+
`critical_contact_integrity` and `manual_submit_boundary` are universal review proofs and must be reported for every review-ready run. Their observations are value-free: never include email, phone, applicant name, answer values, page text, or local paths. `job_identity_match` is required only when the queue/run includes it; its value-free observation must also omit company, role, URL, and requisition.
|
|
33
38
|
|
|
34
39
|
## Final reporting
|
|
35
40
|
|