@salesforce/afv-skills 1.58.0 → 1.60.0
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/package.json +1 -1
- package/skills/agentforce-architecture-analyze/SKILL.md +66 -17
- package/skills/agentforce-architecture-analyze/assets/cli/show_access_token.yaml +4 -7
- package/skills/agentforce-architecture-analyze/references/contract.json +1 -1
- package/skills/agentforce-architecture-analyze/scripts/emit_env.py +2 -2
- package/skills/agentforce-architecture-analyze/scripts/main.py +40 -44
- package/skills/agentforce-architecture-analyze/scripts/rest_client.py +11 -3
- package/skills/agentforce-architecture-analyze/scripts/sf_cli.py +77 -10
- package/skills/agentforce-architecture-analyze/scripts/tests/test_end_to_end_fixture.py +1 -0
- package/skills/agentforce-architecture-analyze/scripts/tests/test_flow_children_inflation.py +1 -0
- package/skills/agentforce-architecture-analyze/scripts/tests/test_main_pipeline.py +53 -2
- package/skills/agentforce-architecture-analyze/scripts/tests/test_resolve_creds.py +184 -36
- package/skills/agentforce-architecture-analyze/scripts/tests/test_rest_client.py +17 -0
- package/skills/agentforce-architecture-analyze/scripts/tests/test_sf_cli.py +165 -11
- package/skills/agentforce-architecture-analyze/scripts/tests/test_skill_md_pipeline_block.py +319 -0
- package/skills/agentforce-bot-upgrade/SKILL.md +3 -1
- package/skills/agentforce-d360-analyze/SKILL.md +28 -10
- package/skills/agentforce-d360-analyze/scripts/dc.py +200 -50
- package/skills/agentforce-d360-analyze/scripts/discover_sessions.py +10 -6
- package/skills/agentforce-d360-analyze/scripts/fetch_dc.py +22 -6
- package/skills/agentforce-d360-analyze/scripts/resolve_session.py +21 -13
- package/skills/agentforce-d360-analyze/scripts/tests/test_dc_and_resolve_session.py +298 -76
- package/skills/agentforce-d360-analyze/scripts/tests/test_default_target_org.py +432 -0
- package/skills/agentforce-d360-analyze/scripts/tests/test_discover_sessions.py +1 -1
- package/skills/agentforce-d360-analyze/scripts/tests/test_resolve_session_main.py +9 -3
- package/skills/agentforce-d360-analyze/scripts/tests/test_skill_md_prefix_block.py +176 -0
- package/skills/agentforce-generate/SKILL.md +21 -1
- package/skills/agentforce-generate/references/known-issues.md +51 -1
- package/skills/agentforce-generate/references/legacy-nga-regression-eval.md +547 -0
- package/skills/agentforce-generate/references/upgrade-legacy-agent-to-agentscript.md +577 -0
- package/skills/agentforce-generate/scripts/upgrade-legacy-nga-analyze.py +404 -0
- package/skills/agentforce-generate/scripts/upgrade-legacy-nga-run-trials.sh +58 -0
- package/skills/agentforce-persona-generate/SKILL.md +505 -0
- package/skills/agentforce-persona-generate/assets/checklist-template.md +100 -0
- package/skills/agentforce-persona-generate/assets/persona-encoding-template.md +252 -0
- package/skills/agentforce-persona-generate/assets/persona-template.md +229 -0
- package/skills/agentforce-persona-generate/assets/sample-dialog-template.md +50 -0
- package/skills/agentforce-persona-generate/assets/workshop-outline-template.md +97 -0
- package/skills/agentforce-persona-generate/examples/concierge-persona.md +189 -0
- package/skills/agentforce-persona-generate/examples/concierge-sample-dialog.md +70 -0
- package/skills/agentforce-persona-generate/examples/drover-persona.md +194 -0
- package/skills/agentforce-persona-generate/examples/drover-sample-dialog.md +62 -0
- package/skills/agentforce-persona-generate/examples/steady-hand-persona.md +188 -0
- package/skills/agentforce-persona-generate/examples/steady-hand-sample-dialog.md +68 -0
- package/skills/agentforce-persona-generate/examples/yt-persona.md +195 -0
- package/skills/agentforce-persona-generate/examples/yt-sample-dialog.md +66 -0
- package/skills/agentforce-persona-generate/references/attributions.md +36 -0
- package/skills/agentforce-persona-generate/references/interaction-design-notes.md +30 -0
- package/skills/agentforce-persona-generate/references/persona-encoding-guide.md +566 -0
- package/skills/agentforce-persona-generate/references/persona-framework.md +796 -0
- package/skills/agentforce-persona-generate/references/refinement-mappings.md +31 -0
- package/skills/agentforce-persona-generate/references/scoring-rubric.md +58 -0
- package/skills/agentforce-persona-generate/references/voice-catalog.json +2327 -0
- package/skills/agentforce-persona-generate/references/workshop-question-policy.md +90 -0
- package/skills/agentforce-persona-generate/references/workshop-scenario-seeds.md +119 -0
- package/skills/agentforce-persona-generate/scripts/parse_agent_yaml.py +223 -0
- package/skills/consumer-goods-accruals-datakit-deploy/SKILL.md +17 -27
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/setup.js +7 -17
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/00-check-prerequisites.js +47 -63
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/01-download-static-resource.js +10 -13
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/02-replace-org-id.js +46 -46
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/03-deploy-metadata.js +4 -21
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/06-create-dataspace.js +58 -82
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/07-deploy-accruals-reports.js +16 -315
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/08-deploy-ui.js +9 -24
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/09-completion.js +44 -44
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/utils.js +11 -63
- package/skills/consumer-goods-rtr-datacloud-export-configure/SKILL.md +2 -1
- package/skills/consumer-goods-tpe-dashboard-configure/SKILL.md +2 -1
- package/skills/consumer-goods-tpe-dashboard-custom-kpi-configure/SKILL.md +2 -1
- package/skills/consumer-goods-tpe-datakit-deploy/SKILL.md +2 -1
- package/skills/data360-schema-get/SKILL.md +3 -3
- package/skills/design-systems-slds-validate/references/report-format.md +1 -1
- package/skills/dx-app-analytics-query/SKILL.md +8 -8
- package/skills/dx-org-manage/SKILL.md +1 -0
- package/skills/dx-org-shape-manage/SKILL.md +1 -0
- package/skills/dx-org-snapshot-manage/SKILL.md +172 -0
- package/skills/dx-org-snapshot-manage/examples/create_error_output.json +16 -0
- package/skills/dx-org-snapshot-manage/examples/create_success_output.json +19 -0
- package/skills/dx-org-snapshot-manage/examples/delete_output.json +9 -0
- package/skills/dx-org-snapshot-manage/examples/get_output.json +19 -0
- package/skills/dx-org-snapshot-manage/examples/list_output.json +36 -0
- package/skills/dx-org-snapshot-manage/references/cli_flags.md +179 -0
- package/skills/dx-org-snapshot-manage/references/snapshot_usage.md +55 -0
- package/skills/dx-org-trial-expiration-check/SKILL.md +1 -1
- package/skills/dx-org-trial-expiration-check/scripts/check_expiration.sh +2 -2
- package/skills/education-cloud-student-recruitment-agent-configure/SKILL.md +7 -5
- package/skills/education-cloud-student-recruitment-agent-configure/references/prerequisites.md +11 -11
- package/skills/experience-content-media-image-generate/SKILL.md +83 -0
- package/skills/experience-content-media-image-generate/scripts/download-image.sh +84 -0
- package/skills/experience-ui-bundle-app-coordinate/SKILL.md +12 -12
- package/skills/experience-ui-bundle-app-coordinate/references/phase-completion-validation.md +3 -3
- package/skills/experience-ui-bundle-app-coordinate/references/phase-execution-pattern.md +9 -7
- package/skills/experience-ui-bundle-app-coordinate/scripts/check-dangling-refs.mjs +132 -0
- package/skills/experience-ui-bundle-app-coordinate/scripts/check-hosting-target.mjs +62 -0
- package/skills/experience-ui-bundle-app-coordinate/scripts/check-phase-1-complete.mjs +71 -0
- package/skills/experience-ui-bundle-app-coordinate/scripts/check-phase-4-complete.mjs +95 -0
- package/skills/experience-ui-bundle-app-coordinate/scripts/check-phase-6-ready.mjs +78 -0
- package/skills/experience-ui-bundle-app-coordinate/scripts/check-prerequisites.mjs +21 -0
- package/skills/experience-ui-bundle-app-coordinate/scripts/check-sfdx-project.mjs +28 -0
- package/skills/experience-ui-bundle-custom-app-generate/SKILL.md +3 -1
- package/skills/experience-ui-bundle-custom-app-generate/scripts/resolve-uibundle-path.mjs +40 -0
- package/skills/experience-ui-bundle-frontend-generate/SKILL.md +7 -5
- package/skills/experience-ui-bundle-frontend-generate/scripts/check-preconditions.mjs +70 -0
- package/skills/experience-ui-bundle-frontend-generate/scripts/resolve-ui-bundle.mjs +76 -0
- package/skills/experience-ui-bundle-frontend-generate/scripts/verify-rules.mjs +115 -0
- package/skills/experience-ui-bundle-metadata-generate/SKILL.md +7 -3
- package/skills/experience-ui-bundle-metadata-generate/references/angular-metadata-generate.md +1 -1
- package/skills/experience-ui-bundle-metadata-generate/references/react-metadata-generate.md +1 -1
- package/skills/experience-ui-bundle-metadata-generate/scripts/check-api-version.mjs +43 -0
- package/skills/experience-ui-bundle-metadata-generate/scripts/detect-framework.mjs +131 -0
- package/skills/experience-ui-bundle-metadata-generate/scripts/verify-bundle-location.mjs +152 -0
- package/skills/experience-ui-bundle-mfa-configure/references/branding.md +10 -6
- package/skills/external-diagram-mermaid-generate/SKILL.md +12 -12
- package/skills/field-service-data-capture-migrate/references/component-mapping.md +1 -1
- package/skills/field-service-data-capture-migrate/references/fsm-dc-capability-catalog.md +1 -2
- package/skills/field-service-data-capture-migrate/references/known-deploy-errors.md +1 -0
- package/skills/field-service-data-capture-migrate/references/transformer-rules.md +1 -1
- package/skills/field-service-data-capture-migrate/scripts/transform_flow.py +12 -1
- package/skills/field-service-data-capture-migrate/scripts/vendor/build_flow.py +4 -1
- package/skills/field-service-setup-orchestrator-get/SKILL.md +0 -1
- package/skills/insurance-brokerage-agency-billing-configure/SKILL.md +233 -0
- package/skills/insurance-brokerage-agency-billing-configure/references/accounting-setup.md +117 -0
- package/skills/insurance-brokerage-agency-billing-configure/references/billing-rules-config.md +237 -0
- package/skills/insurance-brokerage-agency-billing-configure/references/billing-settings.md +271 -0
- package/skills/insurance-brokerage-agency-billing-configure/references/demo-policy-config.md +138 -0
- package/skills/insurance-brokerage-agency-billing-configure/references/org-defaults.md +64 -0
- package/skills/insurance-brokerage-agency-billing-configure/references/permissions-config.md +226 -0
- package/skills/insurance-brokerage-agency-billing-configure/references/prerequisites.md +142 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +3 -2
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-execution-state-and-recovery.md +3 -3
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-overview.md +68 -45
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-visit-creation-data.md +17 -18
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +16 -3
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-creation-data.md +16 -13
- package/skills/marketing-message-test/SKILL.md +194 -0
- package/skills/marketing-message-test/references/api-contract.md +333 -0
- package/skills/omnistudio-callable-apex-generate/SKILL.md +3 -3
- package/skills/omnistudio-datamapper-generate/SKILL.md +6 -6
- package/skills/omnistudio-datapacks-deploy/SKILL.md +11 -11
- package/skills/omnistudio-dependencies-analyze/SKILL.md +5 -5
- package/skills/omnistudio-epc-catalog-generate/SKILL.md +6 -6
- package/skills/omnistudio-flexcard-generate/SKILL.md +6 -6
- package/skills/omnistudio-integration-procedure-generate/SKILL.md +6 -6
- package/skills/omnistudio-omniscript-generate/SKILL.md +6 -5
- package/skills/platform-apex-logs-debug/SKILL.md +1 -0
- package/skills/platform-metadata-deploy/SKILL.md +1 -0
- package/skills/platform-models-api-configure/SKILL.md +5 -5
- package/skills/platform-soql-query/SKILL.md +1 -0
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +17 -6
- package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +107 -192
- package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone-action.json +6 -6
- package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +49 -20
- package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +4 -4
- package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +224 -290
- package/skills/service-itsm-agentic-setup-incident-sla-configure/references/workflow.md +295 -0
- package/skills/service-itsm-incident-priority-configure/SKILL.md +1 -1
- package/skills/service-itsm-slack-itservice-configure/SKILL.md +2 -1
- package/skills/service-itsm-slack-itservice-configure/references/manage-slack-connection.md +2 -2
- package/skills/service-omni-channel-setup-coordinate/SKILL.md +20 -13
- package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +5 -55
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +0 -38
- package/skills/service-omni-command-center-analyze/SKILL.md +2 -3
- package/skills/service-omni-command-center-analyze/scripts/analyze.sh +3 -3
- package/skills/tableau-next-asset-create/README.md +65 -0
- package/skills/tableau-next-asset-create/SKILL.md +409 -0
- package/skills/tableau-next-asset-create/references/admin-users.md +149 -0
- package/skills/tableau-next-asset-create/references/ai-readiness.md +355 -0
- package/skills/tableau-next-asset-create/references/alerts.md +111 -0
- package/skills/tableau-next-asset-create/references/build-workflow.md +819 -0
- package/skills/tableau-next-asset-create/references/dashboard-authoring.md +302 -0
- package/skills/tableau-next-asset-create/references/dashboard-design-principles.md +352 -0
- package/skills/tableau-next-asset-create/references/data-transform-gotchas.md +210 -0
- package/skills/tableau-next-asset-create/references/data-understanding.md +387 -0
- package/skills/tableau-next-asset-create/references/deep-reference-index.md +74 -0
- package/skills/tableau-next-asset-create/references/destructive-operations.md +62 -0
- package/skills/tableau-next-asset-create/references/edit-dashboard.md +273 -0
- package/skills/tableau-next-asset-create/references/edit-visualization.md +529 -0
- package/skills/tableau-next-asset-create/references/empty-source-handling.md +104 -0
- package/skills/tableau-next-asset-create/references/ingest-and-metric-gotchas.md +638 -0
- package/skills/tableau-next-asset-create/references/insight-bundle.md +69 -0
- package/skills/tableau-next-asset-create/references/large-flat-file-handling.md +331 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/_envelope.json +54 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/_shared-widget-fields.json +122 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/add_page.json +29 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/delete_page.json +27 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/discard_dashboard.json +11 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/move_widget.json +30 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/move_widget_to_page.json +30 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/place_widget_on_page.json +30 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/remove_widget_from_dashboard.json +16 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/remove_widget_from_page.json +26 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/rename_page.json +28 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/save_dashboard.json +18 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/undo.json +11 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_button_widget.json +69 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_container_widget.json +88 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_filter_widget.json +146 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_metric_widget.json +78 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_navigation_widget.json +80 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_parameter_widget.json +160 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_text_widget.json +103 -0
- package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_visualization_widget.json +68 -0
- package/skills/tableau-next-asset-create/references/sdm-tool-reference.md +721 -0
- package/skills/tableau-next-asset-create/references/semantic-query-and-enrichment.md +355 -0
- package/skills/tableau-next-asset-create/references/shared-gates.md +322 -0
- package/skills/tableau-next-asset-create/references/sharing-and-promotion.md +306 -0
- package/skills/tableau-next-asset-create/references/tasks/ai-readiness-audit.md +51 -0
- package/skills/tableau-next-asset-create/references/tasks/analyze-data.md +48 -0
- package/skills/tableau-next-asset-create/references/tasks/build-dashboard.md +66 -0
- package/skills/tableau-next-asset-create/references/tasks/build-end-to-end.md +73 -0
- package/skills/tableau-next-asset-create/references/tasks/create-data-transform.md +51 -0
- package/skills/tableau-next-asset-create/references/tasks/create-viz.md +58 -0
- package/skills/tableau-next-asset-create/references/tasks/edit-dashboard.md +80 -0
- package/skills/tableau-next-asset-create/references/tasks/edit-sdm.md +106 -0
- package/skills/tableau-next-asset-create/references/tasks/edit-viz.md +40 -0
- package/skills/tableau-next-asset-create/references/tasks/enrich-model.md +68 -0
- package/skills/tableau-next-asset-create/references/tasks/ingest-flat-file.md +68 -0
- package/skills/tableau-next-asset-create/references/tasks/manage-alert.md +35 -0
- package/skills/tableau-next-asset-create/references/tasks/profile-dataset.md +47 -0
- package/skills/tableau-next-asset-create/references/tasks/promote-or-reuse.md +46 -0
- package/skills/tableau-next-asset-create/references/tasks/provision-user.md +39 -0
- package/skills/tableau-next-asset-create/references/tasks/query-model.md +92 -0
- package/skills/tableau-next-asset-create/references/tasks/review-verified-questions.md +42 -0
- package/skills/tableau-next-asset-create/references/tasks/share-asset.md +37 -0
- package/skills/tableau-next-asset-create/references/verified-questions.md +125 -0
- package/skills/tableau-next-asset-create/references/viz-authoring.md +992 -0
- package/skills/experience-ui-bundle-app-coordinate/scripts/check-dangling-refs.sh +0 -42
- package/skills/experience-ui-bundle-app-coordinate/scripts/check-hosting-target.sh +0 -26
- package/skills/experience-ui-bundle-app-coordinate/scripts/check-phase-1-complete.sh +0 -35
- package/skills/experience-ui-bundle-app-coordinate/scripts/check-phase-4-complete.sh +0 -47
- package/skills/experience-ui-bundle-app-coordinate/scripts/check-phase-6-ready.sh +0 -33
- package/skills/experience-ui-bundle-app-coordinate/scripts/check-prerequisites.sh +0 -20
- package/skills/experience-ui-bundle-app-coordinate/scripts/check-sfdx-project.sh +0 -15
- package/skills/experience-ui-bundle-custom-app-generate/scripts/resolve-uibundle-path.sh +0 -30
- package/skills/experience-ui-bundle-frontend-generate/scripts/check-preconditions.sh +0 -37
- package/skills/experience-ui-bundle-frontend-generate/scripts/resolve-ui-bundle.sh +0 -66
- package/skills/experience-ui-bundle-frontend-generate/scripts/verify-rules.sh +0 -58
- package/skills/experience-ui-bundle-metadata-generate/scripts/check-api-version.sh +0 -30
- package/skills/experience-ui-bundle-metadata-generate/scripts/detect-framework.sh +0 -73
- package/skills/experience-ui-bundle-metadata-generate/scripts/verify-bundle-location.sh +0 -118
- package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/predefined-incident-policy.json +0 -102
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Analyze collected legacy/draft preview trials and emit the comparison report.
|
|
3
|
+
|
|
4
|
+
DIVISION OF LABOR:
|
|
5
|
+
* Rubric #1 (trace / action equality) is MECHANICAL — it compares structured
|
|
6
|
+
identifiers pulled from the local plan traces (subagent topic, ordered
|
|
7
|
+
action names). This is objective equality, so the script does it.
|
|
8
|
+
* Rubric #2 (response discrepancy) is a SEMANTIC judgment — data fidelity,
|
|
9
|
+
formatting, verbosity, tone, spec compliance. This CANNOT be done by regex
|
|
10
|
+
or heuristics. The script does NOT score it. It emits the verbatim responses
|
|
11
|
+
plus an analysis prompt for an LLM judge (or human) to complete.
|
|
12
|
+
|
|
13
|
+
ORACLE MODEL (Rubric #1): legacy is a BASELINE, not the oracle. Three references:
|
|
14
|
+
spec = expected_subagent/expected_actions (authored from intent) — ORACLE
|
|
15
|
+
legacy = what the old agent actually did — descriptive baseline, can be WRONG
|
|
16
|
+
draft = the NGA under test
|
|
17
|
+
We classify the spec-legacy-draft triangle instead of gating on draft==legacy:
|
|
18
|
+
legacy==spec & draft==spec -> PASS
|
|
19
|
+
legacy==spec & draft!=spec -> DRAFT-REGRESSION (fix draft)
|
|
20
|
+
legacy!=spec & draft==spec -> PASS + LEGACY-DEFECT (draft fixed it)
|
|
21
|
+
legacy!=spec & draft!=spec & l==d -> SPEC-REVIEW (spec likely stale)
|
|
22
|
+
legacy!=spec & draft!=spec & l!=d -> ADJUDICATE (LLM judge decides)
|
|
23
|
+
If no spec is supplied the utterance is SPEC-UNVERIFIED: fall back to the
|
|
24
|
+
legacy-vs-draft baseline diff + judge.
|
|
25
|
+
|
|
26
|
+
DRIFT (across iterations): an accepted BASELINE file records the last
|
|
27
|
+
spec-approved (subagent, actions) per utterance. Each run diffs the DRAFT
|
|
28
|
+
signature against it — a change flags DRIFT even if it still matches the spec,
|
|
29
|
+
catching unintended agentscript drift. Re-baseline deliberately (REBASELINE=1)
|
|
30
|
+
when instructions intentionally change, so intended changes update truth
|
|
31
|
+
instead of firing false regressions.
|
|
32
|
+
|
|
33
|
+
Nothing here is agent- or utterance-specific: no field labels, no domain
|
|
34
|
+
vocabulary, no expected values baked in (expected_*/baseline are per run/repo).
|
|
35
|
+
|
|
36
|
+
Rubric #1 source — the LOCAL plan trace `sf agent preview end` writes to
|
|
37
|
+
.sfdx/agents/<agentId>/sessions/<sid>/traces/<planId>.json
|
|
38
|
+
Both sides populate it on CLI>=2.149.9 (@salesforce/agents>=2.0.4). We read
|
|
39
|
+
`topic`/`intent` (subagent) and ordered FunctionStep entries (action name/input/
|
|
40
|
+
status), for legacy (ReAct: UpdateTopicStep+topic) and draft (ScriptAgent:
|
|
41
|
+
TransitionStep). Names match by substring containment (name_match), since legacy
|
|
42
|
+
names append a definition-id suffix of arbitrary format.
|
|
43
|
+
|
|
44
|
+
Usage: analyze.py <run_dir> <utt_id> [expected_subagent] [expected_actions_csv]
|
|
45
|
+
expected_* are OPTIONAL. When omitted, only observed traces + the
|
|
46
|
+
legacy-vs-draft cross-check + drift are reported (no vs-spec triangle verdict).
|
|
47
|
+
Traces live under `.sfdx/agents`; the tool auto-locates it by walking up from
|
|
48
|
+
cwd (then from <run_dir>), so it runs from any directory inside the repo. Only
|
|
49
|
+
if no `.sfdx/agents` exists on either path does a subject report a (correct)
|
|
50
|
+
UNVERIFIED-TRACE. <run_dir> may be relative (e.g. agent-eval/runs/<id>).
|
|
51
|
+
Env (optional):
|
|
52
|
+
EVAL_ORG label only.
|
|
53
|
+
DEMO_CONTACT if set, the first draft action input is checked for this contactId.
|
|
54
|
+
EVAL_BASELINE path to the accepted-baseline JSON (default agent-eval/baseline.json).
|
|
55
|
+
REBASELINE if "1", write the current draft signature into the baseline for
|
|
56
|
+
this utterance (deliberate, human-approved re-baseline) and exit.
|
|
57
|
+
Writes <run_dir>/report.md and prints it.
|
|
58
|
+
"""
|
|
59
|
+
import json, sys, re, glob, os, datetime
|
|
60
|
+
|
|
61
|
+
RUN = sys.argv[1]
|
|
62
|
+
UTT = sys.argv[2] if len(sys.argv) > 2 else "utterance"
|
|
63
|
+
EXP_SUB = sys.argv[3] if len(sys.argv) > 3 and sys.argv[3] else None
|
|
64
|
+
EXP_ACTS = [a for a in (sys.argv[4].split(",") if len(sys.argv) > 4 else []) if a]
|
|
65
|
+
ORG = os.environ.get("EVAL_ORG", "target-org")
|
|
66
|
+
DEMO_CONTACT = os.environ.get("DEMO_CONTACT") # None => no contact-binding assertion
|
|
67
|
+
BASELINE_FILE = os.environ.get("EVAL_BASELINE", os.path.join("agent-eval", "baseline.json"))
|
|
68
|
+
REBASELINE = os.environ.get("REBASELINE") == "1"
|
|
69
|
+
|
|
70
|
+
# Locate `.sfdx/agents` by walking UP from cwd (then from the run dir) so the
|
|
71
|
+
# tool works no matter which directory it's invoked from — otherwise a wrong cwd
|
|
72
|
+
# silently yields a false UNVERIFIED-TRACE for every subject.
|
|
73
|
+
def _find_sfdx_agents():
|
|
74
|
+
for start in (os.getcwd(), os.path.abspath(RUN)):
|
|
75
|
+
d = start
|
|
76
|
+
while True:
|
|
77
|
+
cand = os.path.join(d, ".sfdx", "agents")
|
|
78
|
+
if os.path.isdir(cand):
|
|
79
|
+
return cand
|
|
80
|
+
parent = os.path.dirname(d)
|
|
81
|
+
if parent == d:
|
|
82
|
+
break
|
|
83
|
+
d = parent
|
|
84
|
+
# Fall back to the cwd-relative path; parse_trace() then reports "not found".
|
|
85
|
+
return os.path.join(os.getcwd(), ".sfdx", "agents")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
SFDX_AGENTS = _find_sfdx_agents()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def load(subj):
|
|
92
|
+
out = []
|
|
93
|
+
for f in sorted(glob.glob(f"{RUN}/{subj}/{UTT}/trial-*/trial.json")):
|
|
94
|
+
with open(f) as fh:
|
|
95
|
+
out.append(json.load(fh))
|
|
96
|
+
return out
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# ---- Trace parsing (mechanical rubric #1) -----------------------------------
|
|
100
|
+
def find_trace(plan_id):
|
|
101
|
+
if not plan_id:
|
|
102
|
+
return None
|
|
103
|
+
hits = glob.glob(f"{SFDX_AGENTS}/**/traces/{plan_id}.json", recursive=True)
|
|
104
|
+
return hits[0] if hits else None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# Legacy topic/action names carry a definition-id suffix appended to the base
|
|
108
|
+
# name (e.g. Get_All_Loan_Application_Details_179KX000000YehM), while the NGA
|
|
109
|
+
# draft uses the clean base name. The suffix format is NOT fixed (length/charset
|
|
110
|
+
# vary), so a regex strip is unreliable. Instead treat two names as the same
|
|
111
|
+
# entity when either is a substring of the other (order-independent, since the
|
|
112
|
+
# suffix may land on either side). Compared case-insensitively.
|
|
113
|
+
def name_match(a, b):
|
|
114
|
+
if a is None or b is None:
|
|
115
|
+
return a == b
|
|
116
|
+
a, b = a.lower(), b.lower()
|
|
117
|
+
return a in b or b in a
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def names_match(xs, ys):
|
|
121
|
+
return len(xs) == len(ys) and all(name_match(x, y) for x, y in zip(xs, ys))
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def signature(topic, action_names):
|
|
125
|
+
"""Route+action signature = [subagent] + ordered actions. Compared with
|
|
126
|
+
names_match so legacy definition-id suffixes are tolerated."""
|
|
127
|
+
return [topic] + list(action_names or [])
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def triangle_verdict(leg_sig, draft_sig, spec_sig):
|
|
131
|
+
"""Classify the spec-legacy-draft triangle. Returns (verdict, note).
|
|
132
|
+
spec_sig is None when no expected_* was supplied (SPEC-UNVERIFIED)."""
|
|
133
|
+
if not spec_sig or spec_sig == [None]:
|
|
134
|
+
same = names_match(leg_sig, draft_sig)
|
|
135
|
+
return ("SPEC-UNVERIFIED",
|
|
136
|
+
"no spec supplied — "
|
|
137
|
+
+ ("draft matches legacy baseline" if same
|
|
138
|
+
else "draft diverges from legacy baseline; judge must decide"))
|
|
139
|
+
ls = names_match(leg_sig, spec_sig)
|
|
140
|
+
ds = names_match(draft_sig, spec_sig)
|
|
141
|
+
if ls and ds:
|
|
142
|
+
return ("PASS", "legacy, draft, and spec all agree")
|
|
143
|
+
if ls and not ds:
|
|
144
|
+
return ("DRAFT-REGRESSION", "legacy matched the spec but the draft does not — fix the draft")
|
|
145
|
+
if ds and not ls:
|
|
146
|
+
return ("PASS + LEGACY-DEFECT",
|
|
147
|
+
"draft matches the spec; legacy diverged — legacy was wrong, the migration fixed it")
|
|
148
|
+
# neither matches the spec
|
|
149
|
+
if names_match(leg_sig, draft_sig):
|
|
150
|
+
return ("SPEC-REVIEW", "legacy and draft agree with each other but not the spec — the spec is likely stale/wrong")
|
|
151
|
+
return ("ADJUDICATE", "three-way disagreement (spec vs legacy vs draft) — LLM judge must adjudicate with traces + responses")
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def load_baseline():
|
|
155
|
+
try:
|
|
156
|
+
with open(BASELINE_FILE) as fh:
|
|
157
|
+
return json.load(fh)
|
|
158
|
+
except Exception:
|
|
159
|
+
return {}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def save_baseline(bl):
|
|
163
|
+
os.makedirs(os.path.dirname(BASELINE_FILE) or ".", exist_ok=True)
|
|
164
|
+
with open(BASELINE_FILE, "w") as fh:
|
|
165
|
+
json.dump(bl, fh, indent=2, sort_keys=True)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def parse_trace(plan_id):
|
|
169
|
+
"""Return normalized trace evidence, or a reason it's unavailable.
|
|
170
|
+
|
|
171
|
+
Handles BOTH local trace shapes:
|
|
172
|
+
- draft (ScriptAgent): routing via TransitionStep{from_agent,to_agent}
|
|
173
|
+
- legacy (ProductionAgent, CLI>=2.149.9 / @salesforce/agents>=2.0.4):
|
|
174
|
+
routing via UpdateTopicStep + `topic`; names carry a definitionId suffix.
|
|
175
|
+
Legacy populates only when the SDK fetches the v1.1 preview plans endpoint;
|
|
176
|
+
older SDKs (<=2.0.1) stub getTrace()->undefined and write `{}` (empty plan).
|
|
177
|
+
Names are kept RAW here; equality is decided by name_match() (substring),
|
|
178
|
+
not by stripping the suffix.
|
|
179
|
+
"""
|
|
180
|
+
tf = find_trace(plan_id)
|
|
181
|
+
if not tf:
|
|
182
|
+
return {"available": False, "reason": "trace file not found"}
|
|
183
|
+
try:
|
|
184
|
+
with open(tf) as fh:
|
|
185
|
+
d = json.load(fh)
|
|
186
|
+
except Exception as e:
|
|
187
|
+
return {"available": False, "reason": f"unreadable: {e}"}
|
|
188
|
+
if not isinstance(d, dict) or not d.get("plan"):
|
|
189
|
+
return {"available": False,
|
|
190
|
+
"reason": "empty plan ({}) — SDK too old? need CLI>=2.149.9 (@salesforce/agents>=2.0.4)"}
|
|
191
|
+
transitions, actions = [], []
|
|
192
|
+
for s in d["plan"]:
|
|
193
|
+
if s.get("type") == "TransitionStep": # draft shape
|
|
194
|
+
dd = s.get("data", {})
|
|
195
|
+
transitions.append((dd.get("from_agent"), dd.get("to_agent")))
|
|
196
|
+
if s.get("type") == "FunctionStep": # both shapes
|
|
197
|
+
fn = s.get("function", {})
|
|
198
|
+
out = fn.get("output", {})
|
|
199
|
+
actions.append({
|
|
200
|
+
"name": fn.get("name"),
|
|
201
|
+
"input": fn.get("input"),
|
|
202
|
+
"status": out.get("__action_execution_status__") if isinstance(out, dict) else None,
|
|
203
|
+
})
|
|
204
|
+
topic = d.get("topic")
|
|
205
|
+
# Legacy routes via UpdateTopicStep rather than TransitionStep; synthesize a
|
|
206
|
+
# transition to the topic so the report row is uniform across both shapes.
|
|
207
|
+
if not transitions and topic:
|
|
208
|
+
transitions = [("Agent Router", topic)]
|
|
209
|
+
return {
|
|
210
|
+
"available": True,
|
|
211
|
+
"topic": topic,
|
|
212
|
+
"intent": d.get("intent"),
|
|
213
|
+
"transitions": transitions,
|
|
214
|
+
"actions": actions,
|
|
215
|
+
"action_names": [a["name"] for a in actions],
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
leg, dr = load("legacy"), load("draft")
|
|
220
|
+
if not leg or not dr:
|
|
221
|
+
sys.exit(f"missing trials: legacy={len(leg)} draft={len(dr)}")
|
|
222
|
+
wc = lambda t: len((t or "").split())
|
|
223
|
+
|
|
224
|
+
# Trace evidence per subject (use trial 1's planId as representative).
|
|
225
|
+
leg_tr = parse_trace(leg[0].get("planId"))
|
|
226
|
+
dr_tr = parse_trace(dr[0].get("planId"))
|
|
227
|
+
|
|
228
|
+
# Signatures (route + ordered actions) for the triangle + drift comparisons.
|
|
229
|
+
leg_sig = signature(leg_tr.get("topic"), leg_tr.get("action_names")) if leg_tr["available"] else None
|
|
230
|
+
draft_sig = signature(dr_tr.get("topic"), dr_tr.get("action_names")) if dr_tr["available"] else None
|
|
231
|
+
spec_sig = signature(EXP_SUB, EXP_ACTS) if (EXP_SUB or EXP_ACTS) else None
|
|
232
|
+
|
|
233
|
+
# --- Deliberate re-baseline: record the current DRAFT signature and exit. -----
|
|
234
|
+
# This is the human-approved "the draft is now correct; make it the baseline"
|
|
235
|
+
# action. Use after an intentional instruction change (or first approval).
|
|
236
|
+
if REBASELINE:
|
|
237
|
+
if not dr_tr["available"]:
|
|
238
|
+
sys.exit("REBASELINE refused: draft trace unavailable (cannot capture a signature).")
|
|
239
|
+
bl = load_baseline()
|
|
240
|
+
bl[UTT] = {
|
|
241
|
+
"subagent": dr_tr["topic"],
|
|
242
|
+
"actions": dr_tr["action_names"],
|
|
243
|
+
"approved_at": datetime.datetime.now().isoformat(timespec="seconds"),
|
|
244
|
+
"approved_from_run": RUN,
|
|
245
|
+
}
|
|
246
|
+
save_baseline(bl)
|
|
247
|
+
print(f"Re-baselined `{UTT}` in {BASELINE_FILE}: subagent={dr_tr['topic']} actions={dr_tr['action_names']}")
|
|
248
|
+
sys.exit(0)
|
|
249
|
+
|
|
250
|
+
# Triangle verdict (spec vs legacy vs draft) + drift vs accepted baseline.
|
|
251
|
+
tri_verdict, tri_note = (triangle_verdict(leg_sig, draft_sig, spec_sig)
|
|
252
|
+
if (leg_tr["available"] and dr_tr["available"])
|
|
253
|
+
else ("UNVERIFIED-TRACE", "one or both traces unavailable"))
|
|
254
|
+
baseline = load_baseline()
|
|
255
|
+
base_entry = baseline.get(UTT)
|
|
256
|
+
if base_entry and draft_sig:
|
|
257
|
+
base_sig = signature(base_entry.get("subagent"), base_entry.get("actions"))
|
|
258
|
+
drift = not names_match(base_sig, draft_sig)
|
|
259
|
+
else:
|
|
260
|
+
base_sig, drift = None, None # None => no baseline recorded yet
|
|
261
|
+
|
|
262
|
+
L = []
|
|
263
|
+
p = L.append
|
|
264
|
+
p(f"# Legacy vs NGA Regression Report — utterance `{UTT}`\n")
|
|
265
|
+
p(f"- Run: `{RUN}` | Org: `{ORG}` | Trials: {len(leg)} each")
|
|
266
|
+
p(f"- Legacy latency (ms): {[r['latency_ms'] for r in leg]} | Draft: {[r['latency_ms'] for r in dr]}")
|
|
267
|
+
p(f"- content-safe (all): legacy {all(r['isContentSafe'] for r in leg)} | draft {all(r['isContentSafe'] for r in dr)}")
|
|
268
|
+
p(f"- word count (per trial): legacy {[wc(r['response']) for r in leg]} | draft {[wc(r['response']) for r in dr]}")
|
|
269
|
+
p(f"- Legacy planIds: {[r['planId'] for r in leg]}")
|
|
270
|
+
p(f"- Draft planIds: {[r['planId'] for r in dr]}\n")
|
|
271
|
+
|
|
272
|
+
# ---- Rubric #1 — mechanical -------------------------------------------------
|
|
273
|
+
p("## Rubric #1 — Trace / action equality (MECHANICAL, from local planId trace)\n")
|
|
274
|
+
if EXP_SUB or EXP_ACTS:
|
|
275
|
+
p(f"Expected (from suite): subagent `{EXP_SUB}`, actions `{EXP_ACTS}`.\n")
|
|
276
|
+
else:
|
|
277
|
+
p("_No expected subagent/actions supplied — reporting observed traces and the "
|
|
278
|
+
"legacy-vs-draft cross-check only._\n")
|
|
279
|
+
p("| subject | trace | subagent (topic) | router transition | ordered actions | verdict |")
|
|
280
|
+
p("|---|---|---|---|---|---|")
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def trace_row(name, tr):
|
|
284
|
+
if not tr["available"]:
|
|
285
|
+
return f"| {name} | ❌ {tr['reason']} | — | — | — | **UNVERIFIED-TRACE** |"
|
|
286
|
+
trans = " → ".join(str(x) for x in tr["transitions"][-1]) if tr["transitions"] else "(none)"
|
|
287
|
+
acts = ", ".join(a["name"] for a in tr["actions"]) or "(none)"
|
|
288
|
+
if EXP_SUB or EXP_ACTS:
|
|
289
|
+
sub_ok = name_match(tr["topic"], EXP_SUB) if EXP_SUB else True
|
|
290
|
+
act_ok = names_match(tr["action_names"], EXP_ACTS) if EXP_ACTS else True
|
|
291
|
+
verdict = "TRACE-VERIFIED ✅" if (sub_ok and act_ok) else "TRACE-DIVERGENT ⚠️"
|
|
292
|
+
else:
|
|
293
|
+
verdict = "(observed)"
|
|
294
|
+
return f"| {name} | ✅ local | {tr['topic']} | {trans} | {acts} | **{verdict}** |"
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
p(trace_row("legacy", leg_tr))
|
|
298
|
+
p(trace_row("draft", dr_tr))
|
|
299
|
+
if leg_tr["available"] and dr_tr["available"]:
|
|
300
|
+
same_sub = name_match(leg_tr["topic"], dr_tr["topic"])
|
|
301
|
+
same_acts = names_match(leg_tr["action_names"], dr_tr["action_names"])
|
|
302
|
+
p(f"\n**Legacy vs draft:** subagent {'✅ match' if same_sub else '❌ differ'} "
|
|
303
|
+
f"(legacy `{leg_tr['topic']}` vs draft `{dr_tr['topic']}`) · "
|
|
304
|
+
f"actions {'✅ match' if same_acts else '❌ differ'} "
|
|
305
|
+
f"(legacy `{leg_tr['action_names']}` vs draft `{dr_tr['action_names']}`) → "
|
|
306
|
+
f"**{'ROUTE+ACTION EQUIVALENT' if (same_sub and same_acts) else 'TRACE-DIVERGENT'}**")
|
|
307
|
+
# ---- Triangle verdict (spec vs legacy vs draft) + drift ---------------------
|
|
308
|
+
p("\n### Oracle triangle (spec vs legacy vs draft)\n")
|
|
309
|
+
p("| reference | subagent | actions |")
|
|
310
|
+
p("|---|---|---|")
|
|
311
|
+
p(f"| spec (oracle) | {EXP_SUB or '_none supplied_'} | {EXP_ACTS or '_none supplied_'} |")
|
|
312
|
+
p(f"| legacy (baseline) | {leg_tr.get('topic') if leg_tr['available'] else '—'} | "
|
|
313
|
+
f"{leg_tr.get('action_names') if leg_tr['available'] else '—'} |")
|
|
314
|
+
p(f"| draft (under test) | {dr_tr.get('topic') if dr_tr['available'] else '—'} | "
|
|
315
|
+
f"{dr_tr.get('action_names') if dr_tr['available'] else '—'} |")
|
|
316
|
+
p(f"\n**Triangle verdict: `{tri_verdict}`** — {tri_note}\n")
|
|
317
|
+
p("> Legacy is a descriptive baseline, NOT the oracle: it can be wrong or miss "
|
|
318
|
+
"an action. The spec (expected_*) is the oracle. `PASS + LEGACY-DEFECT` means "
|
|
319
|
+
"the draft matches the spec while legacy diverged — the migration fixed a legacy "
|
|
320
|
+
"defect. `DRAFT-REGRESSION` means legacy was right and the draft broke it. "
|
|
321
|
+
"`SPEC-REVIEW`/`ADJUDICATE` escalate to the LLM judge.\n")
|
|
322
|
+
|
|
323
|
+
# Drift vs the accepted baseline (unintended-change detector).
|
|
324
|
+
if drift is None:
|
|
325
|
+
p(f"**Drift:** no accepted baseline recorded for `{UTT}` yet "
|
|
326
|
+
f"(set it deliberately with `REBASELINE=1`). File: `{BASELINE_FILE}`.\n")
|
|
327
|
+
elif drift:
|
|
328
|
+
p(f"**Drift: ⚠️ DETECTED** — the draft signature changed vs the accepted "
|
|
329
|
+
f"baseline (`{base_entry.get('approved_at')}`).\n"
|
|
330
|
+
f"- baseline: subagent `{base_entry.get('subagent')}` actions `{base_entry.get('actions')}`\n"
|
|
331
|
+
f"- current draft: subagent `{dr_tr.get('topic')}` actions `{dr_tr.get('action_names')}`\n"
|
|
332
|
+
f"\n> If this change was intended, re-approve with `REBASELINE=1`. Otherwise the "
|
|
333
|
+
f"draft drifted from its last spec-approved behavior — investigate.\n")
|
|
334
|
+
else:
|
|
335
|
+
p(f"**Drift:** ✅ none — draft matches the accepted baseline "
|
|
336
|
+
f"(`{base_entry.get('approved_at')}`).\n")
|
|
337
|
+
|
|
338
|
+
p("\n> **Local, symmetric trace.** Both sides write a full local plan trace at "
|
|
339
|
+
"`.sfdx/agents/<agentId>/sessions/<sid>/traces/<planId>.json` "
|
|
340
|
+
"(`topic`=subagent + ordered `FunctionStep` action name/input/status, "
|
|
341
|
+
"type-agnostic across Apex/Flow/Prompt). Requires **CLI >=2.149.9 "
|
|
342
|
+
"(@salesforce/agents>=2.0.4)**; older SDKs stubbed `ProductionAgent.getTrace()` "
|
|
343
|
+
"and wrote `{}`. Legacy names carry a definition-id suffix of arbitrary format; "
|
|
344
|
+
"names match by substring containment (either side), not suffix-stripping.\n")
|
|
345
|
+
|
|
346
|
+
if dr_tr["available"] and dr_tr["actions"]:
|
|
347
|
+
ci = dr_tr["actions"][0].get("input")
|
|
348
|
+
if DEMO_CONTACT:
|
|
349
|
+
ok = isinstance(ci, dict) and ci.get("contactId") == DEMO_CONTACT
|
|
350
|
+
p(f"Draft first action input: `{json.dumps(ci)}` — "
|
|
351
|
+
f"{'✅ bound to DEMO_CONTACT' if ok else '⚠️ not bound to DEMO_CONTACT'}\n")
|
|
352
|
+
else:
|
|
353
|
+
p(f"Draft first action input: `{json.dumps(ci)}` (set DEMO_CONTACT env to assert binding)\n")
|
|
354
|
+
|
|
355
|
+
# ---- Responses (verbatim, all trials) ---------------------------------------
|
|
356
|
+
p("## Responses (verbatim)\n")
|
|
357
|
+
for subj, rows in (("Legacy", leg), ("Draft NGA", dr)):
|
|
358
|
+
p(f"### {subj}\n")
|
|
359
|
+
for r in rows:
|
|
360
|
+
p(f"**Trial {r['trial']}** (planId `{r['planId']}`):\n```\n{r['response']}\n```\n")
|
|
361
|
+
|
|
362
|
+
# ---- Rubric #2 — LLM analysis prompt (NOT scored by this script) ------------
|
|
363
|
+
p("## Rubric #2 — Response analysis (LLM judge — complete this)\n")
|
|
364
|
+
p("> The script does not score responses. An LLM (or human) must read the "
|
|
365
|
+
"verbatim responses above and answer the rubric below. Base every claim on the "
|
|
366
|
+
"actual text; do not assume domain-specific fields.\n")
|
|
367
|
+
drift_ctx = ("no baseline recorded yet" if drift is None
|
|
368
|
+
else "DRIFTED from accepted baseline" if drift
|
|
369
|
+
else "matches accepted baseline")
|
|
370
|
+
p(f"> **Mechanical context for the judge (Rubric #1):** triangle verdict "
|
|
371
|
+
f"`{tri_verdict}` — {tri_note}; drift status: {drift_ctx}. Treat the triangle "
|
|
372
|
+
f"verdict as the routing/action ground truth: legacy is a baseline, not the "
|
|
373
|
+
f"oracle. If the verdict is `PASS + LEGACY-DEFECT`, do not penalize the draft "
|
|
374
|
+
f"for differing from legacy — legacy was wrong. If `DRAFT-REGRESSION`, the draft "
|
|
375
|
+
f"broke a behavior legacy got right. If `SPEC-REVIEW`/`ADJUDICATE`, you are the "
|
|
376
|
+
f"tie-breaker — decide which behavior is actually correct from the responses.\n")
|
|
377
|
+
p("""You are comparing a **legacy** agent against its **migrated NGA draft** for
|
|
378
|
+
the same utterance. Using ONLY the response texts above plus the Rubric #1 trace
|
|
379
|
+
facts, produce:
|
|
380
|
+
|
|
381
|
+
1. **Intra-trial consistency** — for each subject, are the N trials materially
|
|
382
|
+
equivalent (same facts, acceptable wording variance) or do they diverge?
|
|
383
|
+
Pick a canonical trial for each side.
|
|
384
|
+
2. **Data fidelity** — do legacy and draft agree on every concrete fact the
|
|
385
|
+
responses assert (numbers, amounts, dates, statuses, named entities, IDs)?
|
|
386
|
+
List any value the draft changed, dropped, or added. A factual mismatch is a
|
|
387
|
+
**REGRESSION**, not a style difference.
|
|
388
|
+
3. **Formatting** — structural differences: bullets/headers/lists, number and
|
|
389
|
+
money formatting, and any leaked internal field/action API names.
|
|
390
|
+
4. **Verbosity & tone** — length, clarity, empathy, and whether each ends with
|
|
391
|
+
an appropriate follow-up/closing offer.
|
|
392
|
+
5. **Spec compliance** — does each side satisfy the utterance's intent? Note if
|
|
393
|
+
the Rubric #1 action set differs (e.g. one side grounded via a retrieval
|
|
394
|
+
action and the other answered from model knowledge) and whether that changes
|
|
395
|
+
answer quality or trustworthiness.
|
|
396
|
+
6. **Classification** — one of `EQUIVALENT` | `IMPROVED` | `ACCEPTABLE-DIFF` |
|
|
397
|
+
`REGRESSION`, with a one-line justification. If not `EQUIVALENT`/`IMPROVED`,
|
|
398
|
+
give a concrete recommended `.agent` change.
|
|
399
|
+
""")
|
|
400
|
+
|
|
401
|
+
report = "\n".join(L)
|
|
402
|
+
with open(f"{RUN}/report.md", "w") as fh:
|
|
403
|
+
fh.write(report)
|
|
404
|
+
print(report)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Collect N preview trials for one subject × one utterance.
|
|
3
|
+
# Usage: upgrade-legacy-nga-run-trials.sh <subject:legacy|draft> <utt_id> <trials> <outdir> "<utterance>" [mode:simulate|live]
|
|
4
|
+
# mode defaults to `simulate` (safe: no real side effects). Pass `live` ONLY for
|
|
5
|
+
# utterances the caller has opted in per the side-effect safety gate. mode gates
|
|
6
|
+
# the DRAFT only; legacy (--api-name) has no mode flag and always runs live (CLI).
|
|
7
|
+
# Reliable path: start -> send -> end, fresh session per trial,
|
|
8
|
+
# stdout->file (never 2>&1 into a parser).
|
|
9
|
+
# Agent api-name/bundle and org are config, supplied via env (no baked-in agent):
|
|
10
|
+
# EVAL_AGENT legacy Bot api-name AND draft bundle name (they collide by design)
|
|
11
|
+
# EVAL_ORG target-org alias
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
# Force plain output: some shells/CI still get ANSI color codes on `sf --json`,
|
|
14
|
+
# which corrupt the JSON once stdout is redirected to a file (the parser then
|
|
15
|
+
# fails on the escape bytes). NO_COLOR + FORCE_COLOR=0 make the CLI emit clean JSON.
|
|
16
|
+
export NO_COLOR=1 FORCE_COLOR=0
|
|
17
|
+
SUBJECT="$1"; UTT="$2"; N="$3"; OUT="$4"; TEXT="$5"; MODE="${6:-simulate}"
|
|
18
|
+
case "$MODE" in simulate|live) ;; *) echo "mode must be 'simulate' or 'live' (got '$MODE')" >&2; exit 2 ;; esac
|
|
19
|
+
ORG="${EVAL_ORG:?set EVAL_ORG to the target-org alias}"
|
|
20
|
+
AGENT="${EVAL_AGENT:?set EVAL_AGENT to the agent api-name/bundle}"
|
|
21
|
+
# NOTE: --use-live-actions / --simulate-actions are START-ONLY flags.
|
|
22
|
+
# send/end accept only the subject selector (--api-name | --authoring-bundle) + --session-id.
|
|
23
|
+
if [ "$SUBJECT" = "legacy" ]; then
|
|
24
|
+
SEL=(--api-name "$AGENT") # session selector (start/send/end)
|
|
25
|
+
START_SEL=("${SEL[@]}") # legacy (--api-name) has no mode flag; always live (CLI)
|
|
26
|
+
if [ "$MODE" = "simulate" ]; then
|
|
27
|
+
echo " NOTE: legacy (--api-name) has no simulate mode; it runs live. Gate side-effecting utterances upstream." >&2
|
|
28
|
+
fi
|
|
29
|
+
else
|
|
30
|
+
SEL=(--authoring-bundle "$AGENT") # session selector (start/send/end)
|
|
31
|
+
if [ "$MODE" = "live" ]; then
|
|
32
|
+
START_SEL=(--use-live-actions "${SEL[@]}") # mode flag only on start; explicit opt-in
|
|
33
|
+
else
|
|
34
|
+
START_SEL=(--simulate-actions "${SEL[@]}") # default: safe, no real side effects
|
|
35
|
+
fi
|
|
36
|
+
fi
|
|
37
|
+
mkdir -p "$OUT/$SUBJECT/$UTT"
|
|
38
|
+
for i in $(seq 1 "$N"); do
|
|
39
|
+
d="$OUT/$SUBJECT/$UTT/trial-$i"; mkdir -p "$d"
|
|
40
|
+
sf agent preview start --json "${START_SEL[@]}" -o "$ORG" > "$d/start.json" 2>"$d/start.err"
|
|
41
|
+
SID=$(python3 -c "import json;print(json.load(open('$d/start.json'))['result']['sessionId'])")
|
|
42
|
+
t0=$(python3 -c "import time;print(int(time.time()*1000))")
|
|
43
|
+
sf agent preview send --json "${SEL[@]}" --session-id "$SID" -u "$TEXT" -o "$ORG" > "$d/send.json" 2>"$d/send.err"
|
|
44
|
+
t1=$(python3 -c "import time;print(int(time.time()*1000))")
|
|
45
|
+
sf agent preview end --json "${SEL[@]}" --session-id "$SID" -o "$ORG" > "$d/end.json" 2>"$d/end.err" || true
|
|
46
|
+
python3 - "$d" "$SUBJECT" "$UTT" "$i" "$SID" "$((t1-t0))" <<'PY'
|
|
47
|
+
import json,sys
|
|
48
|
+
d,subject,utt,i,sid,lat=sys.argv[1:7]
|
|
49
|
+
s=json.load(open(f"{d}/send.json"))["result"]
|
|
50
|
+
m=s["messages"][0]
|
|
51
|
+
rec={"subject":subject,"utterance_id":utt,"trial":int(i),"sessionId":sid,
|
|
52
|
+
"planId":m.get("planId"),"isContentSafe":m.get("isContentSafe"),
|
|
53
|
+
"response":m.get("message"),"result":m.get("result"),
|
|
54
|
+
"citedReferences":m.get("citedReferences"),"latency_ms":int(lat)}
|
|
55
|
+
json.dump(rec,open(f"{d}/trial.json","w"),indent=1)
|
|
56
|
+
print(f" {subject} {utt} trial {i}: {int(lat)}ms safe={m.get('isContentSafe')} planId={m.get('planId')}")
|
|
57
|
+
PY
|
|
58
|
+
done
|